116 lines
4.5 KiB
C#
116 lines
4.5 KiB
C#
using Disco.Plugins.ServiceTracker.Models;
|
|
using System;
|
|
using System.IO;
|
|
|
|
namespace Disco.Plugins.ServiceTracker.Services
|
|
{
|
|
/// <summary>
|
|
/// Static in-memory cache for the dashboard model.
|
|
/// Instead of running full EF queries + JSON reads + Google Sheet fetches on every page load,
|
|
/// this checks file modification timestamps to detect changes.
|
|
/// If nothing changed, returns the cached model instantly (near-zero CPU/memory cost).
|
|
/// </summary>
|
|
public static class DashboardCache
|
|
{
|
|
private static DashboardViewModel _cachedModel;
|
|
private static DateTime _cacheBuiltAt = DateTime.MinValue;
|
|
private static DateTime _ticketsFileTime = DateTime.MinValue;
|
|
private static DateTime _externalFileTime = DateTime.MinValue;
|
|
private static DateTime _sheetCacheTime = DateTime.MinValue;
|
|
private static DateTime _configFileTime = DateTime.MinValue;
|
|
private static string _lastSortBy;
|
|
private static string _lastFilter;
|
|
private static readonly object _lock = new object();
|
|
|
|
/// <summary>
|
|
/// Maximum age of cache in seconds before forced rebuild, even if no files changed.
|
|
/// Acts as a safety net for database-only changes (e.g. Disco job opened/closed externally).
|
|
/// </summary>
|
|
public static int MaxCacheAgeSeconds = 30;
|
|
|
|
/// <summary>
|
|
/// Returns cached model if still valid, or null if rebuild is needed.
|
|
/// A rebuild is needed when:
|
|
/// - Cache doesn't exist yet
|
|
/// - Any data file has been modified since last build
|
|
/// - Cache is older than MaxCacheAgeSeconds
|
|
/// - Sort/filter parameters changed
|
|
/// </summary>
|
|
public static DashboardViewModel GetIfValid(string dataDirectory, string sortBy, string filterKey)
|
|
{
|
|
lock (_lock)
|
|
{
|
|
if (_cachedModel == null) return null;
|
|
|
|
// Different sort/filter = must rebuild
|
|
if (_lastSortBy != sortBy || _lastFilter != filterKey) return null;
|
|
|
|
// Check age
|
|
var age = (DateTime.Now - _cacheBuiltAt).TotalSeconds;
|
|
if (age > MaxCacheAgeSeconds) return null;
|
|
|
|
// Check if any data files have been modified
|
|
if (HasFileChanged(Path.Combine(dataDirectory, "tickets.json"), ref _ticketsFileTime)) return null;
|
|
if (HasFileChanged(Path.Combine(dataDirectory, "external_tickets.json"), ref _externalFileTime)) return null;
|
|
if (HasFileChanged(Path.Combine(dataDirectory, "sheet_cache.csv"), ref _sheetCacheTime)) return null;
|
|
if (HasFileChanged(Path.Combine(dataDirectory, "config.json"), ref _configFileTime)) return null;
|
|
|
|
return _cachedModel;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Store a freshly built model in the cache.
|
|
/// </summary>
|
|
public static void Store(DashboardViewModel model, string dataDirectory, string sortBy, string filterKey)
|
|
{
|
|
lock (_lock)
|
|
{
|
|
_cachedModel = model;
|
|
_cacheBuiltAt = DateTime.Now;
|
|
_lastSortBy = sortBy;
|
|
_lastFilter = filterKey;
|
|
|
|
// Snapshot current file times
|
|
_ticketsFileTime = GetFileTime(Path.Combine(dataDirectory, "tickets.json"));
|
|
_externalFileTime = GetFileTime(Path.Combine(dataDirectory, "external_tickets.json"));
|
|
_sheetCacheTime = GetFileTime(Path.Combine(dataDirectory, "sheet_cache.csv"));
|
|
_configFileTime = GetFileTime(Path.Combine(dataDirectory, "config.json"));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Force invalidate the cache (called after writes if needed).
|
|
/// </summary>
|
|
public static void Invalidate()
|
|
{
|
|
lock (_lock)
|
|
{
|
|
_cachedModel = null;
|
|
_cacheBuiltAt = DateTime.MinValue;
|
|
}
|
|
}
|
|
|
|
private static bool HasFileChanged(string path, ref DateTime lastKnownTime)
|
|
{
|
|
var currentTime = GetFileTime(path);
|
|
if (currentTime != lastKnownTime)
|
|
{
|
|
lastKnownTime = currentTime;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static DateTime GetFileTime(string path)
|
|
{
|
|
try
|
|
{
|
|
if (File.Exists(path)) return File.GetLastWriteTimeUtc(path);
|
|
}
|
|
catch { }
|
|
return DateTime.MinValue;
|
|
}
|
|
}
|
|
}
|