diff options
author | vnugent <public@vaughnnugent.com> | 2023-10-14 15:50:46 -0400 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-10-14 15:50:46 -0400 |
commit | 3c353afe4dffa3da9c96ef25b02f0004676afe5f (patch) | |
tree | 1e3c5889734c029701205c55fa842552ded81c2b /lib/VNLib.Plugins.Extensions.VNCache/src/DataModel | |
parent | 531baabc8289eeaa2aad63cb0e86cc3dd978d97e (diff) |
experimential expansion and performance changes
Diffstat (limited to 'lib/VNLib.Plugins.Extensions.VNCache/src/DataModel')
3 files changed, 130 insertions, 9 deletions
diff --git a/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/EntityCacheExtensions.cs b/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/EntityCacheExtensions.cs index 73baa4a..363e1c9 100644 --- a/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/EntityCacheExtensions.cs +++ b/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/EntityCacheExtensions.cs @@ -54,12 +54,12 @@ namespace VNLib.Plugins.Extensions.VNCache.DataModel /// <param name="cancellation">A token to cancel the operation</param> /// <returns>A task that completes when the delete operation has compelted</returns> /// <exception cref="ArgumentNullException"></exception> - public static Task DeleteAsync<T>(this IGlobalCacheProvider cache, T entity, CancellationToken cancellation) where T: class, ICacheEntity + public static Task RemoveAsync<T>(this IEntityCache<T> cache, T entity, CancellationToken cancellation) where T: class, ICacheEntity { _ = entity ?? throw new ArgumentNullException(nameof(entity)); _ = cache ?? throw new ArgumentNullException(nameof(entity)); //Delete by its id - return cache.DeleteAsync(entity.Id, cancellation); + return cache.RemoveAsync(entity.Id, cancellation); } /// <summary> @@ -71,27 +71,85 @@ namespace VNLib.Plugins.Extensions.VNCache.DataModel /// <param name="entity">The entity to set at the given key</param> /// <returns>A task that completes when the add/update operation has compelted</returns> /// <exception cref="ArgumentNullException"></exception> - public static Task AddOrUpdateAsync<T>(this IGlobalCacheProvider cache, T entity, CancellationToken cancellation) where T: class, ICacheEntity + public static Task UpsertAsync<T>(this IEntityCache<T> cache, T entity, CancellationToken cancellation) where T: class, ICacheEntity { _ = entity ?? throw new ArgumentNullException(nameof(entity)); _ = cache ?? throw new ArgumentNullException(nameof(cache)); //Add/update with its id - return cache.AddOrUpdateAsync(entity.Id, null, entity, cancellation); + return cache.UpsertAsync(entity.Id, entity, cancellation); + } + + /// <summary> + /// Creates an <see cref="IEntityCache{T}"/> wrapper using the current global cache provider. + /// Understand this will share the same cache store as other stores. Consider creating a scoped cache + /// to avoid key collisions + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="cache"></param> + /// <param name="serialier">The entity data serializer</param> + /// <param name="deserializer">The entity data deserializer</param> + /// <returns>The new <see cref="IEntityCache{T}"/> wrapper instance</returns> + /// <exception cref="ArgumentNullException"></exception> + public static IEntityCache<T> CreateEntityCache<T>(this IGlobalCacheProvider cache, ICacheObjectSerialzer serialier, ICacheObjectDeserialzer deserializer) where T: class + { + _ = cache ?? throw new ArgumentNullException(nameof(cache)); + _ = serialier ?? throw new ArgumentNullException(nameof(serialier)); + _ = deserializer ?? throw new ArgumentNullException(nameof(deserializer)); + + return new EntityCacheImpl<T>(cache, deserializer, serialier); + } + + /// <summary> + /// Creates an <see cref="IEntityCache{T}"/> wrapper using the current global cache provider, + /// with a Json serializer/deserializer + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="cache"></param> + /// <returns>The new <see cref="IEntityCache{T}"/> wrapper using json serialization</returns> + /// <exception cref="ArgumentNullException"></exception> + public static IEntityCache<T> CreateJsonEntityCache<T>(this IGlobalCacheProvider cache) where T: class + { + _ = cache ?? throw new ArgumentNullException(nameof(cache)); + JsonCacheObjectSerializer json = new(); + return CreateEntityCache<T>(cache, json, json); + } + + private sealed class EntityCacheImpl<T> : IEntityCache<T> where T : class + { + private readonly IGlobalCacheProvider _cacheProvider; + private readonly ICacheObjectDeserialzer _cacheObjectDeserialzer; + private readonly ICacheObjectSerialzer _cacheObjectSerialzer; + + public EntityCacheImpl(IGlobalCacheProvider cache, ICacheObjectDeserialzer deserializer, ICacheObjectSerialzer serializer) + { + _cacheProvider = cache; + _cacheObjectDeserialzer = deserializer; + _cacheObjectSerialzer = serializer; + } + + ///<inheritdoc/> + public Task<T?> GetAsync(string id, CancellationToken token = default) => _cacheProvider.GetAsync<T>(id, _cacheObjectDeserialzer, token); + + ///<inheritdoc/> + public Task RemoveAsync(string id, CancellationToken token = default) => _cacheProvider.DeleteAsync(id, token); + + ///<inheritdoc/> + public Task UpsertAsync(string id, T entity, CancellationToken token = default) => _cacheProvider.AddOrUpdateAsync(id, null, entity, _cacheObjectSerialzer, token); } - private sealed class ScopedCacheImpl: ScopedCache { private readonly IGlobalCacheProvider cache; + ///<inheritdoc/> public override bool IsConnected { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => cache.IsConnected; } - + ///<inheritdoc/> protected override ICacheKeyGenerator KeyGen { get; } public ScopedCacheImpl(IGlobalCacheProvider cache, ICacheKeyGenerator keyGen) @@ -171,7 +229,7 @@ namespace VNLib.Plugins.Extensions.VNCache.DataModel } ///<inheritdoc/> - public override Task AddOrUpdateAsync(string key, string? newKey, IObjectData rawData, ICacheObjectSerialzer serialzer, CancellationToken cancellation) + public override Task AddOrUpdateAsync(string key, string? newKey, IObjectData rawData, CancellationToken cancellation) { _ = key ?? throw new ArgumentNullException(nameof(key)); @@ -181,7 +239,7 @@ namespace VNLib.Plugins.Extensions.VNCache.DataModel //If newkey exists, compute the secondary key string? secondary = newKey != null ? KeyGen.ComputedKey(newKey) : null; - return cache.AddOrUpdateAsync(primary, secondary, rawData, serialzer, cancellation); + return cache.AddOrUpdateAsync(primary, secondary, rawData, cancellation); } } } diff --git a/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/IEntityCache.cs b/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/IEntityCache.cs new file mode 100644 index 0000000..e99591b --- /dev/null +++ b/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/IEntityCache.cs @@ -0,0 +1,63 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Extensions.VNCache +* File: IEntityCache.cs +* +* IEntityCache.cs is part of VNLib.Plugins.Extensions.VNCache +* which is part of the larger VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Extensions.VNCache is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Extensions.VNCache is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System.Threading; +using System.Threading.Tasks; + +namespace VNLib.Plugins.Extensions.VNCache.DataModel +{ + /// <summary> + /// Represents a cache that stores referrence type entities + /// </summary> + /// <typeparam name="T">The referrence entity type</typeparam> + public interface IEntityCache<T> where T : class + { + /// <summary> + /// Gets an entity from the cache by its id. Returns null if the entity is not found + /// </summary> + /// <param name="id">The id of the entity to retrieve from the store</param> + /// <param name="token">A token to cancel the operation</param> + /// <returns> The entity if found, null otherwise</returns> + Task<T?> GetAsync(string id, CancellationToken token = default); + + /// <summary> + /// Upserts an entity into the cache by its id. This updates an existing entity + /// or inserts a new one. + /// </summary> + /// <param name="id">The id of the entity to update</param> + /// <param name="entity">A referrence to the entity instance to update</param> + /// <param name="token">A token to cancel the operation</param> + /// <returns>A task that completes when the update has completed successfully</returns> + Task UpsertAsync(string id, T entity, CancellationToken token = default); + + /// <summary> + /// Removes an entity from the cache by its id + /// </summary> + /// <param name="id">The id of the item to remove</param> + /// <param name="token">A token to cancel delete opdation</param> + /// <returns>A task that completes when the item has been deleted successfully</returns> + Task RemoveAsync(string id, CancellationToken token = default); + } + +} diff --git a/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/ScopedCache.cs b/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/ScopedCache.cs index c26fd1a..d949bde 100644 --- a/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/ScopedCache.cs +++ b/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/ScopedCache.cs @@ -65,6 +65,6 @@ namespace VNLib.Plugins.Extensions.VNCache.DataModel public abstract Task GetAsync(string key, IObjectData rawData, CancellationToken cancellation); ///<inheritdoc/> - public abstract Task AddOrUpdateAsync(string key, string? newKey, IObjectData rawData, ICacheObjectSerialzer serialzer, CancellationToken cancellation); + public abstract Task AddOrUpdateAsync(string key, string? newKey, IObjectData rawData, CancellationToken cancellation); } } |