Add caching to QueryAsync and integrate cache reset logic

Introduced a MemoryCache to improve QueryAsync performance by
caching query results, reducing redundant computations. Added
a ResetCache method to reinitialize the cache when settings
are updated. Integrated cache reset calls into settings
property setters to ensure consistency.

Refactored query logic to leverage MemoryCache.GetOrCreateAsync
for streamlined caching. Removed redundant code and debug
logging for improved readability and maintainability. Ensured
thread safety with proper locking mechanisms. Simplified and
consolidated caching logic for better maintainability.
This commit is contained in:
Jack251970 2025-10-26 20:59:29 +08:00
parent e2fa122362
commit f632a4b773
2 changed files with 57 additions and 37 deletions

View file

@ -35,6 +35,9 @@ namespace Flow.Launcher.Plugin.Program
private static readonly List<Result> emptyResults = [];
private static readonly MemoryCacheOptions cacheOptions = new() { SizeLimit = 1560 };
private static MemoryCache cache = new(cacheOptions);
private static readonly string[] commonUninstallerNames =
{
"uninst.exe",
@ -77,45 +80,53 @@ namespace Flow.Launcher.Plugin.Program
public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
{
var resultList = await Task.Run(async () =>
var result = await cache.GetOrCreateAsync(query.Search, async entry =>
{
await _win32sLock.WaitAsync(token);
await _uwpsLock.WaitAsync(token);
try
var resultList = await Task.Run(async () =>
{
// Collect all UWP Windows app directories
var uwpsDirectories = _settings.HideDuplicatedWindowsApp ? _uwps
.Where(uwp => !string.IsNullOrEmpty(uwp.Location)) // Exclude invalid paths
.Where(uwp => uwp.Location.StartsWith(WindowsAppPath, StringComparison.OrdinalIgnoreCase)) // Keep system apps
.Select(uwp => uwp.Location.TrimEnd('\\')) // Remove trailing slash
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray() : null;
return _win32s.Cast<IProgram>()
.Concat(_uwps)
.AsParallel()
.WithCancellation(token)
.Where(HideUninstallersFilter)
.Where(p => HideDuplicatedWindowsAppFilter(p, uwpsDirectories))
.Where(p => p.Enabled)
.Select(p => p.Result(query.Search, Context.API))
.Where(r => string.IsNullOrEmpty(query.Search) || r?.Score > 0)
.ToList();
}
catch (OperationCanceledException)
{
return emptyResults;
}
finally
{
_uwpsLock.Release();
_win32sLock.Release();
}
}, token);
await _win32sLock.WaitAsync(token);
await _uwpsLock.WaitAsync(token);
try
{
// Collect all UWP Windows app directories
var uwpsDirectories = _settings.HideDuplicatedWindowsApp ? _uwps
.Where(uwp => !string.IsNullOrEmpty(uwp.Location)) // Exclude invalid paths
.Where(uwp => uwp.Location.StartsWith(WindowsAppPath, StringComparison.OrdinalIgnoreCase)) // Keep system apps
.Select(uwp => uwp.Location.TrimEnd('\\')) // Remove trailing slash
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray() : null;
resultList = resultList.Count != 0 ? resultList : emptyResults;
return _win32s.Cast<IProgram>()
.Concat(_uwps)
.AsParallel()
.WithCancellation(token)
.Where(HideUninstallersFilter)
.Where(p => HideDuplicatedWindowsAppFilter(p, uwpsDirectories))
.Where(p => p.Enabled)
.Select(p => p.Result(query.Search, Context.API))
.Where(r => string.IsNullOrEmpty(query.Search) || r?.Score > 0)
.ToList();
}
catch (OperationCanceledException)
{
return emptyResults;
}
finally
{
_uwpsLock.Release();
_win32sLock.Release();
}
}, token);
return resultList;
resultList = resultList.Count != 0 ? resultList : emptyResults;
entry.SetSize(resultList.Count);
entry.SetSlidingExpiration(TimeSpan.FromHours(8));
return resultList;
});
return result;
}
private bool HideUninstallersFilter(IProgram program)
@ -305,7 +316,6 @@ namespace Flow.Launcher.Plugin.Program
{
_win32s.Add(win32);
}
Context.API.LogDebug(ClassName, "Cache all Win32 programs");
await Context.API.SaveCacheBinaryStorageAsync<List<Win32>>(Win32CacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath);
lock (_lastIndexTimeLock)
{
@ -337,7 +347,6 @@ namespace Flow.Launcher.Plugin.Program
{
_uwps.Add(uwp);
}
Context.API.LogDebug(ClassName, "Cache all Uwp programs");
await Context.API.SaveCacheBinaryStorageAsync<List<UWPApp>>(UwpCacheName, Context.CurrentPluginMetadata.PluginCacheDirectoryPath);
lock (_lastIndexTimeLock)
{
@ -372,6 +381,13 @@ namespace Flow.Launcher.Plugin.Program
await Task.WhenAll(win32Task, uwpTask).ConfigureAwait(false);
}
internal static void ResetCache()
{
var oldCache = cache;
cache = new MemoryCache(cacheOptions);
oldCache.Dispose();
}
public Control CreateSettingPanel()
{
return new ProgramSetting(Context, _settings);

View file

@ -32,6 +32,7 @@ namespace Flow.Launcher.Plugin.Program.Views
get => _settings.EnableDescription;
set
{
Main.ResetCache();
_settings.EnableDescription = value;
}
}
@ -41,6 +42,7 @@ namespace Flow.Launcher.Plugin.Program.Views
get => _settings.HideAppsPath;
set
{
Main.ResetCache();
_settings.HideAppsPath = value;
}
}
@ -50,6 +52,7 @@ namespace Flow.Launcher.Plugin.Program.Views
get => _settings.HideUninstallers;
set
{
Main.ResetCache();
_settings.HideUninstallers = value;
}
}
@ -59,6 +62,7 @@ namespace Flow.Launcher.Plugin.Program.Views
get => _settings.HideDuplicatedWindowsApp;
set
{
Main.ResetCache();
_settings.HideDuplicatedWindowsApp = value;
}
}