aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-02-14 14:23:53 -0500
committerLibravatar vnugent <public@vaughnnugent.com>2024-02-14 14:23:53 -0500
commit6b87785026ca57d6f41cff87ddbd066362f3cacc (patch)
tree74cddbca5eebcec7948e706bd7b742b19e55eeb6 /lib
parentc0e2a71b7b4081117d87c2c34c1b2afb8d511732 (diff)
Squashed commit of the following:
commit 456ead9bc8b0f61357bae93152ad0403c4940101 Author: vnugent <public@vaughnnugent.com> Date: Tue Feb 13 14:46:35 2024 -0500 fix: #1 shared cluster index on linux & latested core updates commit a481d63f964a5d5204cac2e95141f37f9a28d573 Author: vnugent <public@vaughnnugent.com> Date: Tue Jan 23 15:43:50 2024 -0500 cache extension api tweaks
Diffstat (limited to 'lib')
-rw-r--r--lib/VNLib.Data.Caching.ObjectCache/src/BlobCache.cs76
-rw-r--r--lib/VNLib.Data.Caching.ObjectCache/src/BlobCacheExtensions.cs28
-rw-r--r--lib/VNLib.Data.Caching.ObjectCache/src/BlobCacheLIstener.cs41
-rw-r--r--lib/VNLib.Data.Caching.ObjectCache/src/IMemoryCacheEntryFactory.cs43
-rw-r--r--lib/VNLib.Data.Caching.ObjectCache/src/IPersistantCacheStore.cs10
-rw-r--r--lib/VNLib.Data.Caching/src/ClientExtensions.cs300
-rw-r--r--lib/VNLib.Data.Caching/src/GlobalCacheExtensions.cs48
-rw-r--r--lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs44
-rw-r--r--lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/EntityCacheExtensions.cs21
-rw-r--r--lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/ScopedCache.cs4
-rw-r--r--lib/VNLib.Plugins.Extensions.VNCache/src/VNCacheExtensions.cs21
11 files changed, 334 insertions, 302 deletions
diff --git a/lib/VNLib.Data.Caching.ObjectCache/src/BlobCache.cs b/lib/VNLib.Data.Caching.ObjectCache/src/BlobCache.cs
index 5a425ec..7b2b3b1 100644
--- a/lib/VNLib.Data.Caching.ObjectCache/src/BlobCache.cs
+++ b/lib/VNLib.Data.Caching.ObjectCache/src/BlobCache.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Data.Caching.ObjectCache
@@ -34,10 +34,10 @@ namespace VNLib.Data.Caching.ObjectCache
/// <summary>
/// A general purpose binary data storage
/// </summary>
- public sealed class BlobCache : LRUCache<string, CacheEntry>, IBlobCache, IMemoryCacheEntryFactory
+ public sealed class BlobCache : LRUCache<string, CacheEntry>, IBlobCache
{
private bool disposedValue;
- private IPersistantCacheStore? _persistance;
+ private readonly IPersistantCacheStore? _persistance;
///<inheritdoc/>
public override bool IsReadOnly { get; }
@@ -62,17 +62,12 @@ namespace VNLib.Data.Caching.ObjectCache
public BlobCache(uint bucketId, int maxCapacity, ICacheEntryMemoryManager manager, IPersistantCacheStore? store)
:base(maxCapacity, StringComparer.Ordinal)
{
- if(maxCapacity < 1)
- {
- throw new ArgumentException("The maxium capacity of the store must be a positive integer larger than 0", nameof(maxCapacity));
- }
+ ArgumentOutOfRangeException.ThrowIfLessThan(maxCapacity, 1);
+ ArgumentNullException.ThrowIfNull(manager);
BucketId = bucketId;
-
_persistance = store;
-
- MemoryManager = manager ?? throw new ArgumentNullException(nameof(manager));
-
+ MemoryManager = manager;
MaxCapacity = maxCapacity;
//Update the lookup table size
@@ -88,11 +83,11 @@ namespace VNLib.Data.Caching.ObjectCache
return false;
}
//Use the persistant cache
- return _persistance.OnCacheMiss(BucketId, key, this, out value);
+ return _persistance.OnCacheMiss(BucketId, key, MemoryManager, out value);
}
///<inheritdoc/>
- protected override void Evicted(ref KeyValuePair<string, CacheEntry> evicted)
+ protected override void Evicted(ref readonly KeyValuePair<string, CacheEntry> evicted)
{
try
{
@@ -109,6 +104,8 @@ namespace VNLib.Data.Caching.ObjectCache
///<inheritdoc/>
public bool TryChangeKey(string objectId, string newId, out CacheEntry entry)
{
+ ObjectDisposedException.ThrowIf(disposedValue, this);
+
//Try to get the node at the current key
if (LookupTable.Remove(objectId, out LinkedListNode<KeyValuePair<string, CacheEntry>> ? node))
{
@@ -137,6 +134,8 @@ namespace VNLib.Data.Caching.ObjectCache
///<inheritdoc/>
public override bool Remove(string key)
{
+ ObjectDisposedException.ThrowIf(disposedValue, this);
+
//Remove from persistant store also
_persistance?.OnEntryDeleted(BucketId, key);
@@ -161,33 +160,23 @@ namespace VNLib.Data.Caching.ObjectCache
/// </summary>
public override void Clear()
{
- //Start from first node
- LinkedListNode<KeyValuePair<string, CacheEntry>>? node = List.First;
+ ObjectDisposedException.ThrowIf(disposedValue, this);
- //Classic ll node itteration
- while(node != null)
- {
- //Dispose the cache entry
- node.ValueRef.Value.Dispose();
-
- //Move to next node
- node = node.Next;
- }
-
- //empty all cache entires in the store
- base.Clear();
+ ClearInternal();
}
///<inheritdoc/>
public bool Remove(string objectId, out CacheEntry entry)
{
+ ObjectDisposedException.ThrowIf(disposedValue, this);
+
//Try to get the stored object
- if(TryGetValue(objectId, out entry))
+ if (TryGetValue(objectId, out entry))
{
//remove the entry and bypass the disposal
bool result = base.Remove(objectId);
- Debug.Assert(result == true, "The cache entry was found in the table, but failed to remove");
+ Debug.Assert(result, "The cache entry was found in the table, but failed to remove");
return true;
}
@@ -196,6 +185,25 @@ namespace VNLib.Data.Caching.ObjectCache
return false;
}
+ private void ClearInternal()
+ {
+ //Start from first node
+ LinkedListNode<KeyValuePair<string, CacheEntry>>? node = List.First;
+
+ //Classic ll node itteration
+ while (node != null)
+ {
+ //Dispose the cache entry
+ node.ValueRef.Value.Dispose();
+
+ //Move to next node
+ node = node.Next;
+ }
+
+ //empty all cache entires in the store
+ base.Clear();
+ }
+
///<inheritdoc/>
void Dispose(bool disposing)
{
@@ -203,7 +211,7 @@ namespace VNLib.Data.Caching.ObjectCache
{
if (disposing)
{
- Clear();
+ ClearInternal();
}
disposedValue = true;
}
@@ -216,13 +224,5 @@ namespace VNLib.Data.Caching.ObjectCache
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
-
-
- ///<inheritdoc/>
- CacheEntry IMemoryCacheEntryFactory.CreateEntry(ReadOnlySpan<byte> entryData)
- {
- //Create entry from the internal heap
- return CacheEntry.Create(entryData, MemoryManager);
- }
}
}
diff --git a/lib/VNLib.Data.Caching.ObjectCache/src/BlobCacheExtensions.cs b/lib/VNLib.Data.Caching.ObjectCache/src/BlobCacheExtensions.cs
index ded89d2..1681256 100644
--- a/lib/VNLib.Data.Caching.ObjectCache/src/BlobCacheExtensions.cs
+++ b/lib/VNLib.Data.Caching.ObjectCache/src/BlobCacheExtensions.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Data.Caching.ObjectCache
@@ -144,14 +144,14 @@ namespace VNLib.Data.Caching.ObjectCache
this IBlobCacheTable table,
string objectId,
string? alternateId,
- ObjectDataReader<T> bodyData,
+ ObjectDataGet<T> bodyData,
T state,
DateTime time,
CancellationToken cancellation = default)
{
-
- _ = table ?? throw new ArgumentNullException(nameof(table));
- _ = bodyData ?? throw new ArgumentNullException(nameof(bodyData));
+ ArgumentNullException.ThrowIfNull(table);
+ ArgumentNullException.ThrowIfNull(bodyData);
+ ArgumentException.ThrowIfNullOrWhiteSpace(objectId);
//See if an id change is required
if (string.IsNullOrWhiteSpace(alternateId))
@@ -252,11 +252,27 @@ namespace VNLib.Data.Caching.ObjectCache
/// <param name="objectId">The id of the object to delete</param>
/// <param name="cancellation">A token to cancel the async lock await</param>
/// <returns>A task that completes when the item has been deleted</returns>
- public static async ValueTask<bool> DeleteObjectAsync(this IBlobCacheTable table, string objectId, CancellationToken cancellation = default)
+ public static ValueTask<bool> DeleteObjectAsync(this IBlobCacheTable table, string objectId, CancellationToken cancellation = default)
{
+ ArgumentNullException.ThrowIfNull(table);
+
//Try to get the bucket that the id should belong to
IBlobCacheBucket bucket = table.GetBucket(objectId);
+ return DeleteObjectAsync(bucket, objectId, cancellation);
+ }
+
+ /// <summary>
+ /// Asynchronously deletes a previously stored item
+ /// </summary>
+ /// <param name="bucket"></param>
+ /// <param name="objectId">The id of the object to delete</param>
+ /// <param name="cancellation">A token to cancel the async lock await</param>
+ /// <returns>A task that completes when the item has been deleted</returns>
+ public static async ValueTask<bool> DeleteObjectAsync(this IBlobCacheBucket bucket, string objectId, CancellationToken cancellation = default)
+ {
+ ArgumentNullException.ThrowIfNull(bucket);
+
//Wait for the bucket
IBlobCache cache = await bucket.ManualWaitAsync(cancellation);
diff --git a/lib/VNLib.Data.Caching.ObjectCache/src/BlobCacheLIstener.cs b/lib/VNLib.Data.Caching.ObjectCache/src/BlobCacheLIstener.cs
index 5139746..972bf5e 100644
--- a/lib/VNLib.Data.Caching.ObjectCache/src/BlobCacheLIstener.cs
+++ b/lib/VNLib.Data.Caching.ObjectCache/src/BlobCacheLIstener.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Data.Caching.ObjectCache
@@ -49,44 +49,36 @@ using static VNLib.Data.Caching.Constants;
namespace VNLib.Data.Caching.ObjectCache
{
-
/// <summary>
/// An <see cref="FBMListener"/> for key-value object data caching servers.
/// </summary>
- public class BlobCacheListener<T> : FBMListenerBase<T>, IDisposable
+ /// <remarks>
+ /// Initialzies a new <see cref="BlobCacheListener{T}"/>
+ /// </remarks>
+ /// <param name="cache">The cache table to work from</param>
+ /// <param name="queue">The event queue to publish changes to</param>
+ /// <param name="log">Writes error and debug logging information</param>
+ /// <param name="memoryManager">The heap to alloc FBM buffers and <see cref="CacheEntry"/> cache buffers from</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ public class BlobCacheListener<T>(IBlobCacheTable cache, ICacheListenerEventQueue<T> queue, ILogProvider log, IFBMMemoryManager memoryManager)
+ : FBMListenerBase<T>, IDisposable
{
private bool disposedValue;
///<inheritdoc/>
- protected override ILogProvider Log { get; }
+ protected override ILogProvider Log { get; } = log;
///<inheritdoc/>
- protected override FBMListener Listener { get; }
+ protected override FBMListener Listener { get; } = new(memoryManager);
/// <summary>
/// A queue that stores update and delete events
/// </summary>
- public ICacheListenerEventQueue<T> EventQueue { get; }
+ public ICacheListenerEventQueue<T> EventQueue { get; } = queue ?? throw new ArgumentNullException(nameof(queue));
/// <summary>
/// The Cache store to access data blobs
/// </summary>
- public IBlobCacheTable Cache { get; }
-
- /// <summary>
- /// Initialzies a new <see cref="BlobCacheListener{T}"/>
- /// </summary>
- /// <param name="cache">The cache table to work from</param>
- /// <param name="queue">The event queue to publish changes to</param>
- /// <param name="log">Writes error and debug logging information</param>
- /// <param name="memoryManager">The heap to alloc FBM buffers and <see cref="CacheEntry"/> cache buffers from</param>
- /// <exception cref="ArgumentNullException"></exception>
- public BlobCacheListener(IBlobCacheTable cache, ICacheListenerEventQueue<T> queue, ILogProvider log, IFBMMemoryManager memoryManager)
- {
- Log = log;
- Cache = cache ?? throw new ArgumentNullException(nameof(cache));
- EventQueue = queue ?? throw new ArgumentNullException(nameof(queue));
- Listener = new(memoryManager);
- }
+ public IBlobCacheTable Cache { get; } = cache ?? throw new ArgumentNullException(nameof(cache));
///<inheritdoc/>
protected override async Task ProcessAsync(FBMContext context, T? userState, CancellationToken exitToken)
@@ -254,8 +246,7 @@ namespace VNLib.Data.Caching.ObjectCache
{
if (!disposedValue)
{
- Cache.Dispose();
-
+ Cache.Dispose();
disposedValue = true;
}
}
diff --git a/lib/VNLib.Data.Caching.ObjectCache/src/IMemoryCacheEntryFactory.cs b/lib/VNLib.Data.Caching.ObjectCache/src/IMemoryCacheEntryFactory.cs
deleted file mode 100644
index 1454fc0..0000000
--- a/lib/VNLib.Data.Caching.ObjectCache/src/IMemoryCacheEntryFactory.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-* Copyright (c) 2023 Vaughn Nugent
-*
-* Library: VNLib
-* Package: VNLib.Data.Caching.ObjectCache
-* File: IMemoryCacheEntryFactory.cs
-*
-* IMemoryCacheEntryFactory.cs is part of VNLib.Data.Caching.ObjectCache which
-* is part of the larger VNLib collection of libraries and utilities.
-*
-* VNLib.Data.Caching.ObjectCache 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.Data.Caching.ObjectCache 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;
-
-namespace VNLib.Data.Caching.ObjectCache
-{
- /// <summary>
- /// A factory abstraction that builds <see cref="CacheEntry"/> structures
- /// linked to internally configured memory implementations, for cache
- /// promotions.
- /// </summary>
- public interface IMemoryCacheEntryFactory
- {
- /// <summary>
- /// Creates and initalizes a new <see cref="CacheEntry"/> from the desired object data
- /// </summary>
- /// <param name="entryData">The non-owned memory to copy into the the new <see cref="CacheEntry"/></param>
- /// <returns>The newly initalized <see cref="CacheEntry"/></returns>
- CacheEntry CreateEntry(ReadOnlySpan<byte> entryData);
- }
-}
diff --git a/lib/VNLib.Data.Caching.ObjectCache/src/IPersistantCacheStore.cs b/lib/VNLib.Data.Caching.ObjectCache/src/IPersistantCacheStore.cs
index 40f39f2..3824735 100644
--- a/lib/VNLib.Data.Caching.ObjectCache/src/IPersistantCacheStore.cs
+++ b/lib/VNLib.Data.Caching.ObjectCache/src/IPersistantCacheStore.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Data.Caching.ObjectCache
@@ -54,20 +54,16 @@ namespace VNLib.Data.Caching.ObjectCache
/// This method is called while the bucket lock is held. This call is maded
/// during an <see cref="IBlobCache.Add(string, CacheEntry)"/> method call.
/// </para>
- /// <para>
- /// The <see cref="IMemoryCacheEntryFactory"/> should be used to create the
- /// cache entry for the return value. Once this method returns, the caller owns the new <see cref="CacheEntry"/>
- /// </para>
/// </summary>
/// <param name="key">The key identifying the entry to promot</param>
- /// <param name="factory">The cache entry factory</param>
+ /// <param name="memManager">The cache table memory manager</param>
/// <param name="bucketId">The id of the bucket requesting the operation</param>
/// <param name="entry">The newly created entry when data is found</param>
/// <returns>
/// A value inidcating if the entry was successfully recovered from the persistant storage and
/// was successfully promoted.
/// </returns>
- bool OnCacheMiss(uint bucketId, string key, IMemoryCacheEntryFactory factory, out CacheEntry entry);
+ bool OnCacheMiss(uint bucketId, string key, ICacheEntryMemoryManager memManager, out CacheEntry entry);
/// <summary>
/// Removes an entry from the backing store
diff --git a/lib/VNLib.Data.Caching/src/ClientExtensions.cs b/lib/VNLib.Data.Caching/src/ClientExtensions.cs
index 8273486..f0eee06 100644
--- a/lib/VNLib.Data.Caching/src/ClientExtensions.cs
+++ b/lib/VNLib.Data.Caching/src/ClientExtensions.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Data.Caching
@@ -24,7 +24,7 @@
using System;
using System.Linq;
-using System.Text.Json;
+using System.Buffers;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
@@ -44,14 +44,14 @@ namespace VNLib.Data.Caching
/// </summary>
public static class ClientExtensions
{
+ private readonly record struct AddOrUpdateState<T>(T State, ICacheObjectSerializer Serializer);
- private static readonly JsonCacheObjectSerializer DefaultSerializer = new(256);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void LogDebug(this FBMClient client, string message, params object?[] args)
{
client.Config.DebugLog?.Debug($"[CACHE] : {message}", args);
- }
+ }
/// <summary>
/// Updates the state of the object, and optionally updates the ID of the object. The data
@@ -62,31 +62,45 @@ namespace VNLib.Data.Caching
/// <param name="objectId">The id of the object to update or replace</param>
/// <param name="newId">An optional parameter to specify a new ID for the old object</param>
/// <param name="data">The payload data to serialize and set as the data state of the session</param>
+ /// <param name="serializer">The custom serializer to used to serialze the object to binary</param>
/// <param name="cancellationToken">A token to cancel the operation</param>
/// <returns>A task that resolves when the server responds</returns>
- /// <exception cref="JsonException"></exception>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="InvalidStatusException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="InvalidResponseException"></exception>
/// <exception cref="MessageTooLargeException"></exception>
/// <exception cref="ObjectNotFoundException"></exception>
- public static Task AddOrUpdateObjectAsync<T>(this FBMClient client, string objectId, string? newId, T data, CancellationToken cancellationToken = default)
+ public static Task AddOrUpdateObjectAsync<T>(
+ this FBMClient client,
+ string objectId,
+ string? newId,
+ T data,
+ ICacheObjectSerializer serializer,
+ CancellationToken cancellationToken = default)
{
- //Use the default/json serialzer if not specified
- return AddOrUpdateObjectAsync(client, objectId, newId, data, DefaultSerializer, cancellationToken);
- }
+ ArgumentNullException.ThrowIfNull(serializer);
+
+ //Safe to use struct, should not get promoted to heap during update
+ AddOrUpdateState<T> state = new(data, serializer);
+
+ return AddOrUpdateObjectAsync(client, objectId, newId, Serialize, state, cancellationToken);
+
+ static void Serialize(AddOrUpdateState<T> state, IBufferWriter<byte> finiteWriter)
+ => state.Serializer.Serialize(state.State, finiteWriter);
+ }
+
/// <summary>
/// Updates the state of the object, and optionally updates the ID of the object. The data
/// parameter is serialized, buffered, and streamed to the remote server
/// </summary>
- /// <typeparam name="T"></typeparam>
/// <param name="client"></param>
/// <param name="objectId">The id of the object to update or replace</param>
/// <param name="newId">An optional parameter to specify a new ID for the old object</param>
- /// <param name="data">The payload data to serialize and set as the data state of the session</param>
- /// <param name="serializer">The custom serializer to used to serialze the object to binary</param>
+ /// <param name="data">An <see cref="IObjectData"/> that represents the data to set</param>
/// <param name="cancellationToken">A token to cancel the operation</param>
/// <returns>A task that resolves when the server responds</returns>
/// <exception cref="OutOfMemoryException"></exception>
@@ -95,64 +109,11 @@ namespace VNLib.Data.Caching
/// <exception cref="InvalidResponseException"></exception>
/// <exception cref="MessageTooLargeException"></exception>
/// <exception cref="ObjectNotFoundException"></exception>
- public static async Task AddOrUpdateObjectAsync<T>(
- this FBMClient client,
- string objectId,
- string? newId,
- T data,
- ICacheObjectSerializer serializer,
- CancellationToken cancellationToken = default)
+ public static Task AddOrUpdateObjectAsync(this FBMClient client, string objectId, string? newId, IObjectData data, CancellationToken cancellationToken = default)
{
- _ = client ?? throw new ArgumentNullException(nameof(client));
- _ = serializer ?? throw new ArgumentNullException(nameof(serializer));
-
- client.LogDebug("Updating object {id}, newid {nid}", objectId, newId);
-
- //Rent a new request
- FBMRequest request = client.RentRequest();
- try
- {
- //Set action as get/create
- request.WriteHeader(HeaderCommand.Action, Actions.AddOrUpdate);
-
- //Set object-id header
- request.WriteHeader(Constants.ObjectId, objectId);
-
- //if new-id set, set the new-id header
- if (!string.IsNullOrWhiteSpace(newId))
- {
- request.WriteHeader(Constants.NewObjectId, newId);
- }
-
- //Serialize the message using the request buffer
- serializer.Serialize(data, request.GetBodyWriter());
-
- //Make request
- using FBMResponse response = await client.SendAsync(request, cancellationToken);
- response.ThrowIfNotSet();
-
- //Get the status code
- FBMMessageHeader status = response.Headers.FirstOrDefault(static a => a.Header == HeaderCommand.Status);
-
- //Check status code
- if (status.Value.Equals(ResponseCodes.Okay, StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- else if (status.Value.Equals(ResponseCodes.NotFound, StringComparison.OrdinalIgnoreCase))
- {
- throw new ObjectNotFoundException($"object {objectId} not found on remote server");
- }
-
- //Invalid status
- throw new InvalidStatusException("Invalid status code recived for object upsert request", status.ToString());
- }
- finally
- {
- //Return the request(clears data and reset)
- client.ReturnRequest(request);
- }
+ return AddOrUpdateObjectAsync(client, objectId, newId, static d => d.GetData(), data, cancellationToken);
}
+
/// <summary>
/// Updates the state of the object, and optionally updates the ID of the object. The data
@@ -161,8 +122,9 @@ namespace VNLib.Data.Caching
/// <param name="client"></param>
/// <param name="objectId">The id of the object to update or replace</param>
/// <param name="newId">An optional parameter to specify a new ID for the old object</param>
- /// <param name="data">An <see cref="IObjectData"/> that represents the data to set</param>
+ /// <param name="callback">A callback method that will return the desired object data</param>
/// <param name="cancellationToken">A token to cancel the operation</param>
+ /// <param name="state">The state to be passed to the callback</param>
/// <returns>A task that resolves when the server responds</returns>
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="InvalidStatusException"></exception>
@@ -170,11 +132,31 @@ namespace VNLib.Data.Caching
/// <exception cref="InvalidResponseException"></exception>
/// <exception cref="MessageTooLargeException"></exception>
/// <exception cref="ObjectNotFoundException"></exception>
- public static Task AddOrUpdateObjectAsync(this FBMClient client, string objectId, string? newId, IObjectData data, CancellationToken cancellationToken = default)
+ public static Task AddOrUpdateObjectAsync<T>(
+ this FBMClient client,
+ string objectId,
+ string? newId,
+ ObjectDataGet<T> callback,
+ T state,
+ CancellationToken cancellationToken = default
+ )
{
- return AddOrUpdateObjectAsync(client, objectId, newId, static d => d.GetData(), data, cancellationToken);
+ //Safe to use struct, should not get promoted to heap during update
+ ObjectDataGetState<T> getState = new(state, callback);
+
+ return AddOrUpdateObjectAsync(client, objectId, newId, PutData, getState, cancellationToken);
+
+ //Function to put the data from the callback into the writer
+ static void PutData(ObjectDataGetState<T> state, IBufferWriter<byte> writer)
+ {
+ //Get the data from the callback
+ ReadOnlySpan<byte> data = state.Getter(state.UserState);
+ //Write the data to the writer
+ writer.Write(data);
+ }
}
+
/// <summary>
/// Updates the state of the object, and optionally updates the ID of the object. The data
/// parameter is serialized, buffered, and streamed to the remote server
@@ -186,16 +168,26 @@ namespace VNLib.Data.Caching
/// <param name="cancellationToken">A token to cancel the operation</param>
/// <param name="state">The state to be passed to the callback</param>
/// <returns>A task that resolves when the server responds</returns>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="InvalidStatusException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="InvalidResponseException"></exception>
/// <exception cref="MessageTooLargeException"></exception>
/// <exception cref="ObjectNotFoundException"></exception>
- public static async Task AddOrUpdateObjectAsync<T>(this FBMClient client, string objectId, string? newId, ObjectDataReader<T> callback, T state, CancellationToken cancellationToken = default)
+ public static Task AddOrUpdateObjectAsync<T>(
+ this FBMClient client,
+ string objectId,
+ string? newId,
+ ObjectDataReader<T> callback,
+ T state,
+ CancellationToken cancellationToken = default
+ )
{
- _ = client ?? throw new ArgumentNullException(nameof(client));
- _ = callback ?? throw new ArgumentNullException(nameof(callback));
+ ArgumentNullException.ThrowIfNull(client);
+ ArgumentNullException.ThrowIfNull(callback);
+ ArgumentException.ThrowIfNullOrWhiteSpace(objectId);
client.LogDebug("Updating object {id}, newid {nid}", objectId, newId);
@@ -215,53 +207,84 @@ namespace VNLib.Data.Caching
request.WriteHeader(Constants.NewObjectId, newId);
}
- //Write the message body as the objet data
- request.WriteBody(callback(state));
+ //Write the message body as the object data
+ callback(state, request.GetBodyWriter());
- //Make request
- using FBMResponse response = await client.SendAsync(request, cancellationToken);
- response.ThrowIfNotSet();
-
- //Get the status code
- FBMMessageHeader status = response.Headers.FirstOrDefault(static a => a.Header == HeaderCommand.Status);
+ return ExecAsync(client, request, objectId, cancellationToken);
+ }
+ catch
+ {
+ //Return the request(clears data and reset)
+ client.ReturnRequest(request);
+ throw;
+ }
- //Check status code
- if (status.Value.Equals(ResponseCodes.Okay, StringComparison.OrdinalIgnoreCase))
+ static async Task ExecAsync(FBMClient client, FBMRequest request, string objectId, CancellationToken cancellationToken)
+ {
+ try
{
- return;
+ //Make request
+ using FBMResponse response = await client.SendAsync(request, cancellationToken);
+ response.ThrowIfNotSet();
+
+ //Get the status code
+ FBMMessageHeader status = response.Headers.FirstOrDefault(static a => a.Header == HeaderCommand.Status);
+
+ //Check status code
+ if (status.ValueEquals(ResponseCodes.Okay, StringComparison.OrdinalIgnoreCase))
+ {
+ return;
+ }
+ else if (status.ValueEquals(ResponseCodes.NotFound, StringComparison.OrdinalIgnoreCase))
+ {
+ throw new ObjectNotFoundException($"object {objectId} not found on remote server");
+ }
+
+ //Invalid status
+ throw new InvalidStatusException("Invalid status code recived for object upsert request", status.ToString());
}
- else if (status.Value.Equals(ResponseCodes.NotFound, StringComparison.OrdinalIgnoreCase))
+ finally
{
- throw new ObjectNotFoundException($"object {objectId} not found on remote server");
+ //Return the request(clears data and reset)
+ client.ReturnRequest(request);
}
-
- //Invalid status
- throw new InvalidStatusException("Invalid status code recived for object upsert request", status.ToString());
- }
- finally
- {
- //Return the request(clears data and reset)
- client.ReturnRequest(request);
}
}
+
/// <summary>
- /// Gets an object from the server if it exists, and uses the default serialzer to
- /// recover the object
+ /// Gets an object from the server if it exists
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="client"></param>
/// <param name="objectId">The id of the object to get</param>
/// <param name="cancellationToken">A token to cancel the operation</param>
+ /// <param name="getter">A callback function that computes an object result from binary data</param>
+ /// <param name="state">A user-state parameter to be passed back to the callback function</param>
/// <returns>A task that completes to return the results of the response payload</returns>
- /// <exception cref="JsonException"></exception>
- /// <exception cref="OutOfMemoryException"></exception>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidStatusException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="InvalidResponseException"></exception>
- public static Task<T?> GetObjectAsync<T>(this FBMClient client, string objectId, CancellationToken cancellationToken = default)
+ public static async Task<T?> GetObjectAsync<T, TState>(
+ this FBMClient client,
+ string objectId,
+ GetObjectFromData<T,TState> getter,
+ TState state,
+ CancellationToken cancellationToken = default
+ )
{
- return GetObjectAsync<T>(client, objectId, DefaultSerializer, cancellationToken);
+ ArgumentNullException.ThrowIfNull(getter);
+
+ //Get state will store the object result if successfull get operation
+ GetObjectState<T, TState> st = new(state, getter);
+
+ //Get the object, if successfull, compute the result
+ bool success = await GetObjectAsync(client, objectId, static (s, d) => s.ComputeResult(d), st, cancellationToken);
+
+ //If the get operation failed, return a default value
+ return success ? st.Result : default;
}
/// <summary>
@@ -273,51 +296,17 @@ namespace VNLib.Data.Caching
/// <param name="cancellationToken">A token to cancel the operation</param>
/// <param name="deserialzer">The custom data deserialzer used to deserialze the binary cache result</param>
/// <returns>A task that completes to return the results of the response payload</returns>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidStatusException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="InvalidResponseException"></exception>
- public static async Task<T?> GetObjectAsync<T>(this FBMClient client, string objectId, ICacheObjectDeserializer deserialzer, CancellationToken cancellationToken = default)
+ public static Task<T?> GetObjectAsync<T>(this FBMClient client, string objectId, ICacheObjectDeserializer deserialzer, CancellationToken cancellationToken = default)
{
- _ = client ?? throw new ArgumentNullException(nameof(client));
- _ = deserialzer ?? throw new ArgumentNullException(nameof(deserialzer));
-
- client.LogDebug("Getting object {id}", objectId);
-
- //Rent a new request
- FBMRequest request = client.RentRequest();
- try
- {
- //Set action as get/create
- request.WriteHeader(HeaderCommand.Action, Actions.Get);
-
- //Set object id header
- request.WriteHeader(Constants.ObjectId, objectId);
-
- //Make request
- using FBMResponse response = await client.SendAsync(request, cancellationToken);
- response.ThrowIfNotSet();
-
- //Get the status code
- FBMMessageHeader status = response.Headers.FirstOrDefault(static a => a.Header == HeaderCommand.Status);
-
- //Check ok status code, then its safe to deserialize
- if (status.Value.Equals(ResponseCodes.Okay, StringComparison.Ordinal))
- {
- return deserialzer.Deserialize<T>(response.ResponseBody);
- }
-
- //Object may not exist on the server yet
- if (status.Value.Equals(ResponseCodes.NotFound, StringComparison.Ordinal))
- {
- return default;
- }
-
- throw new InvalidStatusException("Invalid status code recived for object get request", status.ToString());
- }
- finally
- {
- client.ReturnRequest(request);
- }
+ ArgumentNullException.ThrowIfNull(deserialzer);
+
+ //Use the deserialzer to deserialize the data as a state parameter
+ return GetObjectAsync(client, objectId, static (s, d) => s.Deserialize<T>(d), deserialzer, cancellationToken);
}
/// <summary>
@@ -330,6 +319,8 @@ namespace VNLib.Data.Caching
/// <param name="data">An object data instance used to store the found object data</param>
/// <param name="cancellationToken">A token to cancel the operation</param>
/// <returns>A task that completes to return the results of the response payload</returns>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidStatusException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="InvalidResponseException"></exception>
@@ -349,13 +340,16 @@ namespace VNLib.Data.Caching
/// <param name="state">The state parameter to pass to the callback method</param>
/// <param name="cancellationToken">A token to cancel the operation</param>
/// <returns>When complete, true if the object was found, false if not found, and an exception otherwise</returns>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidStatusException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="InvalidResponseException"></exception>
public static async Task<bool> GetObjectAsync<T>(this FBMClient client, string objectId, ObjectDataSet<T> setter, T state, CancellationToken cancellationToken = default)
{
- _ = client ?? throw new ArgumentNullException(nameof(client));
- _ = setter ?? throw new ArgumentNullException(nameof(setter));
+ ArgumentNullException.ThrowIfNull(client);
+ ArgumentNullException.ThrowIfNull(setter);
+ ArgumentException.ThrowIfNullOrWhiteSpace(objectId);
client.LogDebug("Getting object {id}", objectId);
@@ -405,13 +399,16 @@ namespace VNLib.Data.Caching
/// <param name="objectId">The id of the object to update or replace</param>
/// <param name="cancellationToken">A token to cancel the operation</param>
/// <returns>A task that resolves when the operation has completed</returns>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidStatusException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="InvalidResponseException"></exception>
/// <exception cref="ObjectNotFoundException"></exception>
public static async Task<bool> DeleteObjectAsync(this FBMClient client, string objectId, CancellationToken cancellationToken = default)
{
- _ = client ?? throw new ArgumentNullException(nameof(client));
+ ArgumentNullException.ThrowIfNull(client);
+ ArgumentException.ThrowIfNullOrWhiteSpace(objectId);
client.LogDebug("Deleting object {id}", objectId);
@@ -431,11 +428,11 @@ namespace VNLib.Data.Caching
//Get the status code
FBMMessageHeader status = response.Headers.FirstOrDefault(static a => a.Header == HeaderCommand.Status);
- if (status.Value.Equals(ResponseCodes.Okay, StringComparison.Ordinal))
+ if (status.ValueEquals(ResponseCodes.Okay, StringComparison.Ordinal))
{
return true;
}
- else if (status.Value.Equals(ResponseCodes.NotFound, StringComparison.OrdinalIgnoreCase))
+ else if (status.ValueEquals(ResponseCodes.NotFound, StringComparison.OrdinalIgnoreCase))
{
return false;
}
@@ -459,7 +456,8 @@ namespace VNLib.Data.Caching
/// <exception cref="InvalidOperationException"></exception>
public static async Task WaitForChangeAsync(this FBMClient client, WaitForChangeResult change, CancellationToken cancellationToken = default)
{
- _ = change ?? throw new ArgumentNullException(nameof(change));
+ ArgumentNullException.ThrowIfNull(client);
+ ArgumentNullException.ThrowIfNull(change);
//Rent a new request
FBMRequest request = client.RentRequest();
@@ -473,9 +471,9 @@ namespace VNLib.Data.Caching
response.ThrowIfNotSet();
- change.Status = response.Headers.FirstOrDefault(static a => a.Header == HeaderCommand.Status).Value.ToString();
- change.CurrentId = response.Headers.SingleOrDefault(static v => v.Header == Constants.ObjectId).Value.ToString();
- change.NewId = response.Headers.SingleOrDefault(static v => v.Header == Constants.NewObjectId).Value.ToString();
+ change.Status = response.Headers.FirstOrDefault(static a => a.Header == HeaderCommand.Status).GetValueString();
+ change.CurrentId = response.Headers.SingleOrDefault(static v => v.Header == Constants.ObjectId).GetValueString();
+ change.NewId = response.Headers.SingleOrDefault(static v => v.Header == Constants.NewObjectId).GetValueString();
}
finally
{
diff --git a/lib/VNLib.Data.Caching/src/GlobalCacheExtensions.cs b/lib/VNLib.Data.Caching/src/GlobalCacheExtensions.cs
index 586df73..203219c 100644
--- a/lib/VNLib.Data.Caching/src/GlobalCacheExtensions.cs
+++ b/lib/VNLib.Data.Caching/src/GlobalCacheExtensions.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Data.Caching
@@ -26,7 +26,8 @@ using System;
using System.Threading;
using System.Threading.Tasks;
-#pragma warning disable CA1062 // Validate arguments of public methods
+using VNLib.Net.Messaging.FBM;
+using VNLib.Data.Caching.Exceptions;
namespace VNLib.Data.Caching
{
@@ -46,6 +47,7 @@ namespace VNLib.Data.Caching
/// <returns>A task that complets when the object data has been written to the data buffer</returns>
public static Task GetAsync(this IGlobalCacheProvider cache, string key, IObjectData rawData, CancellationToken cancellation)
{
+ ArgumentNullException.ThrowIfNull(cache);
return cache.GetAsync(key, static (cd, data) => cd.SetData(data), rawData, cancellation);
}
@@ -61,6 +63,7 @@ namespace VNLib.Data.Caching
/// <returns>A task that completes when the update operation has compelted</returns>
public static Task AddOrUpdateAsync(this IGlobalCacheProvider cache, string key, string? newKey, IObjectData rawData, CancellationToken cancellation)
{
+ ArgumentNullException.ThrowIfNull(cache);
return cache.AddOrUpdateAsync(key, newKey, static cd => cd.GetData(), rawData, cancellation);
}
@@ -76,10 +79,49 @@ namespace VNLib.Data.Caching
/// <returns>A task that completes when the update operation has compelted</returns>
public static Task AddOrUpdateAsync(this IGlobalCacheProvider cache, string key, string? newKey, ReadOnlyMemory<byte> rawData, CancellationToken cancellation)
{
+ ArgumentNullException.ThrowIfNull(cache);
return cache.AddOrUpdateAsync(key, newKey, static cd => cd.Span, rawData, cancellation);
}
/// <summary>
+ /// Gets an object from the server if it exists
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <typeparam name="TState"></typeparam>
+ /// <param name="cache"></param>
+ /// <param name="objectId">The id of the object to get</param>
+ /// <param name="cancellationToken">A token to cancel the operation</param>
+ /// <param name="getter">A callback function that computes an object result from binary data</param>
+ /// <param name="state">A user-state parameter to be passed back to the callback function</param>
+ /// <returns>A task that completes to return the results of the response payload</returns>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="InvalidStatusException"></exception>
+ /// <exception cref="ObjectDisposedException"></exception>
+ /// <exception cref="InvalidResponseException"></exception>
+ public static async Task<T?> GetAsync<T, TState>(
+ this IGlobalCacheProvider cache,
+ string objectId,
+ GetObjectFromData<T, TState> getter,
+ TState state,
+ CancellationToken cancellationToken = default
+ )
+ {
+ ArgumentNullException.ThrowIfNull(cache);
+ ArgumentNullException.ThrowIfNull(getter);
+
+ //Get state will store the object result if successfull get operation
+ GetObjectState<T, TState> st = new(state, getter);
+
+ //Get the object, if successfull, compute the result
+ await cache.GetAsync(objectId, static (s, d) => s.ComputeResult(d), st, cancellationToken);
+
+ //If the get operation failed, return a default value
+ return st.Result;
+ }
+
+
+ /// <summary>
/// Asynchronously gets a value from the backing cache store
/// </summary>
/// <typeparam name="T"></typeparam>
@@ -89,6 +131,7 @@ namespace VNLib.Data.Caching
/// <returns>The value if found, or null if it does not exist in the store</returns>
public static Task<T?> GetAsync<T>(this IGlobalCacheProvider cache, string key, CancellationToken cancellation)
{
+ ArgumentNullException.ThrowIfNull(cache);
return cache.GetAsync<T>(key, cache.DefaultDeserializer, cancellation);
}
@@ -104,6 +147,7 @@ namespace VNLib.Data.Caching
/// <returns>A task that completes when the update operation has compelted</returns>
public static Task AddOrUpdateAsync<T>(this IGlobalCacheProvider cache, string key, string? newKey, T value, CancellationToken cancellation)
{
+ ArgumentNullException.ThrowIfNull(cache);
return cache.AddOrUpdateAsync(key, newKey, value, cache.DefaultSerializer, cancellation);
}
}
diff --git a/lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs b/lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs
index e04c9e4..1545b99 100644
--- a/lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs
+++ b/lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Data.Caching
@@ -23,6 +23,7 @@
*/
using System;
+using System.Buffers;
using System.Threading;
using System.Threading.Tasks;
@@ -39,12 +40,47 @@ namespace VNLib.Data.Caching
public delegate void ObjectDataSet<T>(T state, ReadOnlySpan<byte> objectData);
/// <summary>
- /// A delegate method that will get the raw objet data from a state object
+ /// A delegate method that will get the raw object data from a state object
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="state">The state object passed to the caller</param>
/// <returns>The raw object data to store in cache</returns>
- public delegate ReadOnlySpan<byte> ObjectDataReader<T>(T state);
+ public delegate ReadOnlySpan<byte> ObjectDataGet<T>(T state);
+
+ /// <summary>
+ /// A delegate method that will write the raw object data to the supplied
+ /// data buffer
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="state">The state object passed to the caller</param>
+ /// <param name="finiteWriter">The finite sized buffer writer use to write object data to</param>
+ public delegate void ObjectDataReader<T>(T state, IBufferWriter<byte> finiteWriter);
+
+ /// <summary>
+ /// A delegate method that will get an object from the raw object data
+ /// </summary>
+ /// <typeparam name="TObject"></typeparam>
+ /// <typeparam name="TState"></typeparam>
+ /// <param name="state">Optional user-state data</param>
+ /// <param name="data">The object data to compute the object result from</param>
+ /// <returns>The resultant object</returns>
+ public delegate TObject GetObjectFromData<TObject, TState>(TState state, ReadOnlySpan<byte> data);
+
+ /// <summary>
+ /// Internal structure used to store a callback and state for the
+ /// a data read/get operation on a cache object
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="UserState">The user-state object to pass</param>
+ /// <param name="Getter">The data get callback function</param>
+ internal readonly record struct ObjectDataGetState<T>(T UserState, ObjectDataGet<T> Getter);
+
+ internal sealed class GetObjectState<T, TState>(TState State, GetObjectFromData<T, TState> Getter)
+ {
+ public T? Result;
+
+ public void ComputeResult(ReadOnlySpan<byte> data) => Result = Getter(State, data);
+ }
/// <summary>
/// A global cache provider interface
@@ -123,6 +159,6 @@ namespace VNLib.Data.Caching
/// <param name="state">The callback state parameter</param>
/// <param name="cancellation">A token to cancel the async operation</param>
/// <returns>A task that completes when the update operation has compelted</returns>
- Task AddOrUpdateAsync<T>(string key, string? newKey, ObjectDataReader<T> callback, T state, CancellationToken cancellation);
+ Task AddOrUpdateAsync<T>(string key, string? newKey, ObjectDataGet<T> callback, T state, CancellationToken cancellation);
}
} \ No newline at end of file
diff --git a/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/EntityCacheExtensions.cs b/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/EntityCacheExtensions.cs
index 562c220..6b39580 100644
--- a/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/EntityCacheExtensions.cs
+++ b/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/EntityCacheExtensions.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Extensions.VNCache
@@ -164,12 +164,13 @@ namespace VNLib.Plugins.Extensions.VNCache.DataModel
/// <param name="factory">The factory callback function to produce a value when a cache miss occurs</param>
/// <param name="cancellation">A token to cancel the operation</param>
/// <returns>A task that completes by returning the entity</returns>
+ /// <exception cref="ArgumentException"></exception>
/// <exception cref="ArgumentNullException"></exception>
public static async Task<T?> GetOrLoadAsync<T>(this IEntityCache<T> cache, string id, Func<string, CancellationToken, Task<T?>> factory, CancellationToken cancellation = default) where T : class
{
- _ = cache ?? throw new ArgumentNullException(nameof(cache));
- _ = id ?? throw new ArgumentNullException(nameof(id));
- _ = factory ?? throw new ArgumentNullException(nameof(factory));
+ ArgumentNullException.ThrowIfNull(cache);
+ ArgumentNullException.ThrowIfNull(factory);
+ ArgumentException.ThrowIfNullOrWhiteSpace(id);
//try to load the value from cache
T? record = await cache.GetAsync(id, cancellation);
@@ -241,7 +242,7 @@ namespace VNLib.Plugins.Extensions.VNCache.DataModel
///<inheritdoc/>
public override Task<bool> DeleteAsync(string key, CancellationToken cancellation)
{
- _ = key ?? throw new ArgumentNullException(nameof(key));
+ ArgumentException.ThrowIfNullOrWhiteSpace(key);
//Compute the key for the id
string scoped = KeyGen.ComputedKey(key);
return Cache.DeleteAsync(scoped, cancellation);
@@ -250,7 +251,7 @@ namespace VNLib.Plugins.Extensions.VNCache.DataModel
///<inheritdoc/>
public override Task<T> GetAsync<T>(string key, ICacheObjectDeserializer deserializer, CancellationToken cancellation)
{
- _ = key ?? throw new ArgumentNullException(nameof(key));
+ ArgumentException.ThrowIfNullOrWhiteSpace(key);
//Compute the key for the id
string scoped = KeyGen.ComputedKey(key);
@@ -261,7 +262,7 @@ namespace VNLib.Plugins.Extensions.VNCache.DataModel
///<inheritdoc/>
public override Task AddOrUpdateAsync<T>(string key, string? newKey, T value, ICacheObjectSerializer serialzer, CancellationToken cancellation)
{
- _ = key ?? throw new ArgumentNullException(nameof(key));
+ ArgumentException.ThrowIfNullOrWhiteSpace(key);
//Compute primary key from id
string primary = KeyGen.ComputedKey(key);
@@ -275,7 +276,7 @@ namespace VNLib.Plugins.Extensions.VNCache.DataModel
///<inheritdoc/>
public override Task GetAsync<T>(string key, ObjectDataSet<T> callback, T state, CancellationToken cancellation)
{
- _ = key ?? throw new ArgumentNullException(nameof(key));
+ ArgumentException.ThrowIfNullOrWhiteSpace(key);
//Compute the key for the id
string scoped = KeyGen.ComputedKey(key);
@@ -284,9 +285,9 @@ namespace VNLib.Plugins.Extensions.VNCache.DataModel
}
///<inheritdoc/>
- public override Task AddOrUpdateAsync<T>(string key, string? newKey, ObjectDataReader<T> callback, T state, CancellationToken cancellation)
+ public override Task AddOrUpdateAsync<T>(string key, string? newKey, ObjectDataGet<T> callback, T state, CancellationToken cancellation)
{
- _ = key ?? throw new ArgumentNullException(nameof(key));
+ ArgumentException.ThrowIfNullOrWhiteSpace(key);
//Compute primary key from id
string primary = KeyGen.ComputedKey(key);
diff --git a/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/ScopedCache.cs b/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/ScopedCache.cs
index 545e194..5107dea 100644
--- a/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/ScopedCache.cs
+++ b/lib/VNLib.Plugins.Extensions.VNCache/src/DataModel/ScopedCache.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Extensions.VNCache
@@ -65,7 +65,7 @@ namespace VNLib.Plugins.Extensions.VNCache.DataModel
public abstract Task GetAsync<T>(string key, ObjectDataSet<T> callback, T state, CancellationToken cancellation);
///<inheritdoc/>
- public abstract Task AddOrUpdateAsync<T>(string key, string? newKey, ObjectDataReader<T> callback, T state, CancellationToken cancellation);
+ public abstract Task AddOrUpdateAsync<T>(string key, string? newKey, ObjectDataGet<T> callback, T state, CancellationToken cancellation);
///<inheritdoc/>
public abstract object GetUnderlyingStore();
diff --git a/lib/VNLib.Plugins.Extensions.VNCache/src/VNCacheExtensions.cs b/lib/VNLib.Plugins.Extensions.VNCache/src/VNCacheExtensions.cs
index d191ab1..c434e22 100644
--- a/lib/VNLib.Plugins.Extensions.VNCache/src/VNCacheExtensions.cs
+++ b/lib/VNLib.Plugins.Extensions.VNCache/src/VNCacheExtensions.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Extensions.VNCache
@@ -112,26 +112,19 @@ namespace VNLib.Plugins.Extensions.VNCache
/// <exception cref="ArgumentNullException"></exception>
public static ScopedCache GetPrefixedCache(this IGlobalCacheProvider cache, string prefix, HashAlg digest = HashAlg.SHA1, HashEncodingMode encoding = HashEncodingMode.Base64)
{
- _ = cache ?? throw new ArgumentNullException(nameof(cache));
- _ = prefix ?? throw new ArgumentNullException(nameof(prefix));
+ ArgumentNullException.ThrowIfNull(cache);
+ ArgumentException.ThrowIfNullOrEmpty(prefix);
//Create simple cache key generator
SimpleCacheKeyImpl keyProv = new(prefix, digest, encoding);
//Create the scoped cache from the simple provider
return cache.GetScopedCache(keyProv);
}
- private sealed class SimpleCacheKeyImpl : ICacheKeyGenerator
+ private sealed class SimpleCacheKeyImpl(string prefix, HashAlg digest, HashEncodingMode encoding) : ICacheKeyGenerator
{
- private readonly string Prefix;
- private readonly HashAlg Digest;
- private readonly HashEncodingMode Encoding;
-
- public SimpleCacheKeyImpl(string prefix, HashAlg digest, HashEncodingMode encoding)
- {
- Prefix = prefix;
- Digest = digest;
- Encoding = encoding;
- }
+ private readonly string Prefix = prefix;
+ private readonly HashAlg Digest = digest;
+ private readonly HashEncodingMode Encoding = encoding;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int ComputeBufferSize(string id) => id.Length + Prefix.Length;