aboutsummaryrefslogtreecommitdiff
path: root/lib/VNLib.Data.Caching/src
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/VNLib.Data.Caching/src
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/VNLib.Data.Caching/src')
-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
3 files changed, 235 insertions, 157 deletions
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