From dc7ad57c845cc9b1b502e5e8b12ce96af4183dc4 Mon Sep 17 00:00:00 2001 From: vman Date: Fri, 18 Nov 2022 17:15:36 -0500 Subject: Add project files. --- VNLib.Data.Caching.Global/GlobalDataCache.cs | 145 +++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 VNLib.Data.Caching.Global/GlobalDataCache.cs (limited to 'VNLib.Data.Caching.Global/GlobalDataCache.cs') diff --git a/VNLib.Data.Caching.Global/GlobalDataCache.cs b/VNLib.Data.Caching.Global/GlobalDataCache.cs new file mode 100644 index 0000000..2c60ae2 --- /dev/null +++ b/VNLib.Data.Caching.Global/GlobalDataCache.cs @@ -0,0 +1,145 @@ +using VNLib.Data.Caching.Global.Exceptions; + +namespace VNLib.Data.Caching.Global +{ + /// + /// A static library for caching data in-process or a remote data + /// cache + /// + public static class GlobalDataCache + { + + private static IGlobalCacheProvider? CacheProvider; + private static CancellationTokenRegistration _reg; + + private static readonly object CacheLock = new(); + private static readonly Dictionary> LocalCache = new(); + + /// + /// Gets a value that indicates if global cache is available + /// + public static bool IsAvailable => CacheProvider != null && CacheProvider.IsConnected; + + /// + /// Sets the backing cache provider for the process-wide global cache + /// + /// The cache provider instance + /// A token that represents the store's validity + public static void SetProvider(IGlobalCacheProvider cacheProvider, CancellationToken statusToken) + { + CacheProvider = cacheProvider ?? throw new ArgumentNullException(nameof(cacheProvider)); + //Remove cache provider when cache provider is no longer valid + _reg = statusToken.Register(Cleanup); + } + + private static void Cleanup() + { + CacheProvider = null; + //Clear local cache + lock (CacheLock) + { + LocalCache.Clear(); + } + _reg.Dispose(); + } + + /// + /// Asynchronously gets a value from the global cache provider + /// + /// + /// The key identifying the object to recover from cache + /// The value if found, or null if it does not exist in the store + /// + /// + public static async Task GetAsync(string key) where T: class + { + //Check local cache first + lock (CacheLock) + { + if (LocalCache.TryGetValue(key, out WeakReference? wr)) + { + //Value is found + if(wr.TryGetTarget(out object? value)) + { + //Value exists and is loaded to local cache + return (T)value; + } + //Value has been collected + else + { + //Remove the key from the table + LocalCache.Remove(key); + } + } + } + //get ref to local cache provider + IGlobalCacheProvider? prov = CacheProvider; + if(prov == null) + { + throw new CacheNotLoadedException("Global cache provider was not found"); + } + //get the value from the store + T? val = await prov.GetAsync(key); + //Only store the value if it was successfully found + if (val != null) + { + //Store in local cache + lock (CacheLock) + { + LocalCache[key] = new WeakReference(val); + } + } + return val; + } + + /// + /// Asynchronously sets (or updates) a cached value in the global cache + /// + /// + /// The key identifying the object to recover from cache + /// The value to set at the given key + /// A task that completes when the update operation has compelted + /// + /// + public static async Task SetAsync(string key, T value) where T : class + { + //Local record is stale, allow it to be loaded from cache next call to get + lock (CacheLock) + { + LocalCache.Remove(key); + } + //get ref to local cache provider + IGlobalCacheProvider? prov = CacheProvider; + if (prov == null) + { + throw new CacheNotLoadedException("Global cache provider was not found"); + } + //set the value in the store + await prov.SetAsync(key, value); + } + + /// + /// Asynchronously deletes an item from cache by its key + /// + /// + /// A task that completes when the delete operation has compelted + /// + /// + public static async Task DeleteAsync(string key) + { + //Delete from local cache + lock (CacheLock) + { + LocalCache.Remove(key); + } + //get ref to local cache provider + IGlobalCacheProvider? prov = CacheProvider; + if (prov == null) + { + throw new CacheNotLoadedException("Global cache provider was not found"); + } + //Delete value from store + await prov.DeleteAsync(key); + } + } +} \ No newline at end of file -- cgit