diff options
Diffstat (limited to 'lib/VNLib.Data.Caching')
-rw-r--r-- | lib/VNLib.Data.Caching/src/ClientExtensions.cs | 297 | ||||
-rw-r--r-- | lib/VNLib.Data.Caching/src/GlobalCacheExtensions.cs | 82 | ||||
-rw-r--r-- | lib/VNLib.Data.Caching/src/ICacheObjectDeserializer.cs (renamed from lib/VNLib.Data.Caching/src/ICacheObjectDeserialzer.cs) | 6 | ||||
-rw-r--r-- | lib/VNLib.Data.Caching/src/ICacheObjectSerializer.cs (renamed from lib/VNLib.Data.Caching/src/ICacheObjectSerialzer.cs) | 6 | ||||
-rw-r--r-- | lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs | 43 | ||||
-rw-r--r-- | lib/VNLib.Data.Caching/src/JsonCacheObjectSerializer.cs | 6 | ||||
-rw-r--r-- | lib/VNLib.Data.Caching/src/WaitForChangeResult.cs | 23 |
7 files changed, 309 insertions, 154 deletions
diff --git a/lib/VNLib.Data.Caching/src/ClientExtensions.cs b/lib/VNLib.Data.Caching/src/ClientExtensions.cs index 946c9b5..a2ec27d 100644 --- a/lib/VNLib.Data.Caching/src/ClientExtensions.cs +++ b/lib/VNLib.Data.Caching/src/ClientExtensions.cs @@ -24,7 +24,6 @@ using System; using System.Linq; -using System.Buffers; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -52,26 +51,7 @@ namespace VNLib.Data.Caching private static void LogDebug(this FBMClient client, string message, params object?[] args) { client.Config.DebugLog?.Debug($"[CACHE] : {message}", args); - } - - /// <summary> - /// Gets an object from the server if it exists, and uses the default serialzer to - /// recover the object - /// </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> - /// <returns>A task that completes to return the results of the response payload</returns> - /// <exception cref="JsonException"></exception> - /// <exception cref="OutOfMemoryException"></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) - { - return GetObjectAsync<T>(client, objectId, DefaultSerializer, cancellationToken); - } + } /// <summary> /// Updates the state of the object, and optionally updates the ID of the object. The data @@ -95,37 +75,58 @@ namespace VNLib.Data.Caching { //Use the default/json serialzer if not specified return AddOrUpdateObjectAsync(client, objectId, newId, data, DefaultSerializer, cancellationToken); - } + } /// <summary> - /// Gets an object from the server if it exists + /// 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 get</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="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> + /// <returns>A task that resolves when the server responds</returns> + /// <exception cref="OutOfMemoryException"></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, ICacheObjectDeserialzer deserialzer, CancellationToken cancellationToken = default) + /// <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) { _ = client ?? throw new ArgumentNullException(nameof(client)); - _ = deserialzer ?? throw new ArgumentNullException(nameof(deserialzer)); + _ = serializer ?? throw new ArgumentNullException(nameof(serializer)); - client.LogDebug("Getting object {id}", objectId); + 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.Get); + request.WriteHeader(HeaderCommand.Action, Actions.AddOrUpdate); - //Set object id header + //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(); @@ -133,22 +134,22 @@ namespace VNLib.Data.Caching //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)) + //Check status code + if (status.Value.Equals(ResponseCodes.Okay, StringComparison.OrdinalIgnoreCase)) { - return deserialzer.Deserialze<T>(response.ResponseBody); + return; } - - //Object may not exist on the server yet - if (status.Value.Equals(ResponseCodes.NotFound, StringComparison.Ordinal)) + else if (status.Value.Equals(ResponseCodes.NotFound, StringComparison.OrdinalIgnoreCase)) { - return default; + throw new ObjectNotFoundException($"object {objectId} not found on remote server"); } - throw new InvalidStatusException("Invalid status code recived for object get request", status.ToString()); + //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); } } @@ -157,12 +158,10 @@ namespace VNLib.Data.Caching /// 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> @@ -171,16 +170,32 @@ 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, - ICacheObjectSerialzer serializer, - CancellationToken cancellationToken = default) + public static Task AddOrUpdateObjectAsync(this FBMClient client, string objectId, string? newId, IObjectData data, CancellationToken cancellationToken = default) + { + 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 + /// parameter is serialized, buffered, and streamed to the remote server + /// </summary> + /// <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="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> + /// <exception cref="ObjectDisposedException"></exception> + /// <exception cref="InvalidResponseException"></exception> + /// <exception cref="MessageTooLargeException"></exception> + /// <exception cref="ObjectNotFoundException"></exception> + public async 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)); - _ = serializer ?? throw new ArgumentNullException(nameof(serializer)); + _ = callback ?? throw new ArgumentNullException(nameof(callback)); client.LogDebug("Updating object {id}, newid {nid}", objectId, newId); @@ -200,11 +215,8 @@ namespace VNLib.Data.Caching request.WriteHeader(Constants.NewObjectId, newId); } - //Get the body writer for the message - IBufferWriter<byte> bodyWriter = request.GetBodyWriter(); - - //Serialize the message - serializer.Serialize(data, bodyWriter); + //Write the message body as the objet data + request.WriteBody(callback(state)); //Make request using FBMResponse response = await client.SendAsync(request, cancellationToken); @@ -234,45 +246,70 @@ namespace VNLib.Data.Caching } /// <summary> - /// Asynchronously deletes an object in the remote store + /// Gets an object from the server if it exists, and uses the default serialzer to + /// recover the object /// </summary> + /// <typeparam name="T"></typeparam> /// <param name="client"></param> - /// <param name="objectId">The id of the object to update or replace</param> + /// <param name="objectId">The id of the object to get</param> /// <param name="cancellationToken">A token to cancel the operation</param> - /// <returns>A task that resolves when the operation has completed</returns> + /// <returns>A task that completes to return the results of the response payload</returns> + /// <exception cref="JsonException"></exception> + /// <exception cref="OutOfMemoryException"></exception> /// <exception cref="InvalidStatusException"></exception> /// <exception cref="ObjectDisposedException"></exception> /// <exception cref="InvalidResponseException"></exception> - /// <exception cref="ObjectNotFoundException"></exception> - public static async Task DeleteObjectAsync(this FBMClient client, string objectId, CancellationToken cancellationToken = default) + public static Task<T?> GetObjectAsync<T>(this FBMClient client, string objectId, CancellationToken cancellationToken = default) + { + return GetObjectAsync<T>(client, objectId, DefaultSerializer, cancellationToken); + } + + /// <summary> + /// 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="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="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) { _ = client ?? throw new ArgumentNullException(nameof(client)); + _ = deserialzer ?? throw new ArgumentNullException(nameof(deserialzer)); + + client.LogDebug("Getting object {id}", objectId); - client.LogDebug("Deleting object {id}", objectId); - //Rent a new request FBMRequest request = client.RentRequest(); try { - //Set action as delete - request.WriteHeader(HeaderCommand.Action, Actions.Delete); - //Set session-id header + //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; + return deserialzer.Deserialize<T>(response.ResponseBody); } - else if(status.Value.Equals(ResponseCodes.NotFound, StringComparison.OrdinalIgnoreCase)) + + //Object may not exist on the server yet + if (status.Value.Equals(ResponseCodes.NotFound, StringComparison.Ordinal)) { - throw new ObjectNotFoundException($"object {objectId} not found on remote server"); + return default; } throw new InvalidStatusException("Invalid status code recived for object get request", status.ToString()); @@ -284,47 +321,54 @@ namespace VNLib.Data.Caching } /// <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 + /// Gets an object from the server if it exists. If data is retreived, it sets + /// the <see cref="IObjectData.SetData(ReadOnlySpan{byte})"/>, if no data is + /// found, this method returns and never calls SetData. /// </summary> /// <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="objectId">The id of the object to get</param> + /// <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 resolves when the server responds</returns> - /// <exception cref="OutOfMemoryException"></exception> + /// <returns>A task that completes to return the results of the response payload</returns> /// <exception cref="InvalidStatusException"></exception> /// <exception cref="ObjectDisposedException"></exception> /// <exception cref="InvalidResponseException"></exception> - /// <exception cref="MessageTooLargeException"></exception> - /// <exception cref="ObjectNotFoundException"></exception> - public async static Task AddOrUpdateObjectAsync(this FBMClient client, string objectId, string? newId, IObjectData data, CancellationToken cancellationToken = default) + public static Task<bool> GetObjectAsync(this FBMClient client, string objectId, IObjectData data, CancellationToken cancellationToken = default) + { + return GetObjectAsync(client, objectId, static (p, d) => p.SetData(d), data, cancellationToken); + } + + /// <summary> + /// Gets an object from the server if it exists. If data is retreived, it sets + /// the <see cref="IObjectData.SetData(ReadOnlySpan{byte})"/>, if no data is + /// found, this method returns and never calls SetData. + /// </summary> + /// <param name="client"></param> + /// <param name="objectId">The id of the object to get</param> + /// <param name="setter">A callback method used to store the recovered object data</param> + /// <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="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)); - _ = data ?? throw new ArgumentNullException(nameof(data)); + _ = setter ?? throw new ArgumentNullException(nameof(setter)); - client.LogDebug("Updating object {id}, newid {nid}", objectId, newId); + 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.AddOrUpdate); + request.WriteHeader(HeaderCommand.Action, Actions.Get); - //Set session-id header + //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); - } - - //Write the message body as the objet data - request.WriteBody(data.GetData()); - //Make request using FBMResponse response = await client.SendAsync(request, cancellationToken); response.ThrowIfNotSet(); @@ -332,54 +376,52 @@ namespace VNLib.Data.Caching //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)) + //Check ok status code, then its safe to deserialize + if (status.Value.Equals(ResponseCodes.Okay, StringComparison.Ordinal)) { - return; + //Write the object data + setter(state, response.ResponseBody); + return true; } - else if (status.Value.Equals(ResponseCodes.NotFound, StringComparison.OrdinalIgnoreCase)) + + //Object may not exist on the server yet + if (status.Value.Equals(ResponseCodes.NotFound, StringComparison.Ordinal)) { - throw new ObjectNotFoundException($"object {objectId} not found on remote server"); + return false; } - //Invalid status - throw new InvalidStatusException("Invalid status code recived for object upsert request", status.ToString()); + throw new InvalidStatusException("Invalid status code recived for object get request", status.ToString()); } finally { - //Return the request(clears data and reset) client.ReturnRequest(request); } } /// <summary> - /// Gets an object from the server if it exists. If data is retreived, it sets - /// the <see cref="IObjectData.SetData(ReadOnlySpan{byte})"/>, if no data is - /// found, this method returns and never calls SetData. + /// Asynchronously deletes an object in the remote store /// </summary> /// <param name="client"></param> - /// <param name="objectId">The id of the object to get</param> + /// <param name="objectId">The id of the object to update or replace</param> /// <param name="cancellationToken">A token to cancel the operation</param> - /// <param name="data">An <see cref="IObjectData"/> that represents the object data to set</param> - /// <returns>A task that completes to return the results of the response payload</returns> + /// <returns>A task that resolves when the operation has completed</returns> /// <exception cref="InvalidStatusException"></exception> /// <exception cref="ObjectDisposedException"></exception> /// <exception cref="InvalidResponseException"></exception> - public static async Task GetObjectAsync(this FBMClient client, string objectId, IObjectData data, CancellationToken cancellationToken = default) + /// <exception cref="ObjectNotFoundException"></exception> + public static async Task<bool> DeleteObjectAsync(this FBMClient client, string objectId, CancellationToken cancellationToken = default) { _ = client ?? throw new ArgumentNullException(nameof(client)); - _ = data ?? throw new ArgumentNullException(nameof(data)); - client.LogDebug("Getting object {id}", objectId); + client.LogDebug("Deleting 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 + //Set action as delete + request.WriteHeader(HeaderCommand.Action, Actions.Delete); + //Set session-id header request.WriteHeader(Constants.ObjectId, objectId); //Make request @@ -389,18 +431,13 @@ namespace VNLib.Data.Caching //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)) { - //Write the object data - data.SetData(response.ResponseBody); - return; + return true; } - - //Object may not exist on the server yet - if (status.Value.Equals(ResponseCodes.NotFound, StringComparison.Ordinal)) + else if (status.Value.Equals(ResponseCodes.NotFound, StringComparison.OrdinalIgnoreCase)) { - return; + return false; } throw new InvalidStatusException("Invalid status code recived for object get request", status.ToString()); @@ -415,10 +452,15 @@ namespace VNLib.Data.Caching /// Dequeues a change event from the server event queue for the current connection, or waits until a change happens /// </summary> /// <param name="client"></param> + /// <param name="change">The instance to store change event data to</param> /// <param name="cancellationToken">A token to cancel the deuque operation</param> /// <returns>A <see cref="WaitForChangeResult"/> that contains information about the modified element</returns> - public static async Task<WaitForChangeResult> WaitForChangeAsync(this FBMClient client, CancellationToken cancellationToken = default) + /// <exception cref="InvalidResponseException"></exception> + /// <exception cref="InvalidOperationException"></exception> + public static async Task WaitForChangeAsync(this FBMClient client, WaitForChangeResult change, CancellationToken cancellationToken = default) { + _ = change ?? throw new ArgumentNullException(nameof(change)); + //Rent a new request FBMRequest request = client.RentRequest(); try @@ -431,12 +473,9 @@ namespace VNLib.Data.Caching response.ThrowIfNotSet(); - return new() - { - Status = response.Headers.FirstOrDefault(static a => a.Header == HeaderCommand.Status).Value.ToString(), - CurrentId = response.Headers.SingleOrDefault(static v => v.Header == Constants.ObjectId).Value.ToString(), - NewId = response.Headers.SingleOrDefault(static v => v.Header == Constants.NewObjectId).Value.ToString() - }; + 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(); } finally { diff --git a/lib/VNLib.Data.Caching/src/GlobalCacheExtensions.cs b/lib/VNLib.Data.Caching/src/GlobalCacheExtensions.cs new file mode 100644 index 0000000..8b23240 --- /dev/null +++ b/lib/VNLib.Data.Caching/src/GlobalCacheExtensions.cs @@ -0,0 +1,82 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Data.Caching +* File: GlobalCacheExtensions.cs +* +* GlobalCacheExtensions.cs is part of VNLib.Data.Caching which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Data.Caching 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 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; +using System.Threading; +using System.Threading.Tasks; + +#pragma warning disable CA1062 // Validate arguments of public methods + +namespace VNLib.Data.Caching +{ + /// <summary> + /// Exports extension methods for the <see cref="IGlobalCacheProvider"/> interface + /// </summary> + public static class GlobalCacheExtensions + { + /// <summary> + /// Asynchronously gets a value from the backing cache store and writes it to the + /// supplied data buffer + /// </summary> + /// <param name="cache"></param> + /// <param name="key">The key identifying the object to recover from cache</param> + /// <param name="rawData">The </param> + /// <param name="cancellation">A token to cancel the async operation</param> + /// <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) + { + return cache.GetAsync(key, static (cd, data) => cd.SetData(data), rawData, cancellation); + } + + /// <summary> + /// Asynchronously sets (or updates) a cached value in the backing cache store + /// from the supplied raw data + /// </summary> + /// <param name="cache"></param> + /// <param name="key">The key identifying the object to recover from cache</param> + /// <param name="newKey">An optional key that will be changed for the new object</param> + /// <param name="cancellation">A token to cancel the async operation</param> + /// <param name="rawData">The raw data to store at the given key</param> + /// <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) + { + return cache.AddOrUpdateAsync(key, newKey, static cd => cd.GetData(), rawData, cancellation); + } + + /// <summary> + /// Asynchronously sets (or updates) a cached value in the backing cache store + /// from the supplied raw data + /// </summary> + /// <param name="cache"></param> + /// <param name="key">The key identifying the object to recover from cache</param> + /// <param name="newKey">An optional key that will be changed for the new object</param> + /// <param name="cancellation">A token to cancel the async operation</param> + /// <param name="rawData">The raw data to store at the given key</param> + /// <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) + { + return cache.AddOrUpdateAsync(key, newKey, static cd => cd.Span, rawData, cancellation); + } + } +}
\ No newline at end of file diff --git a/lib/VNLib.Data.Caching/src/ICacheObjectDeserialzer.cs b/lib/VNLib.Data.Caching/src/ICacheObjectDeserializer.cs index 3cdb395..560d3bf 100644 --- a/lib/VNLib.Data.Caching/src/ICacheObjectDeserialzer.cs +++ b/lib/VNLib.Data.Caching/src/ICacheObjectDeserializer.cs @@ -3,7 +3,7 @@ * * Library: VNLib * Package: VNLib.Data.Caching -* File: ICacheObjectDeserialzer.cs +* File: ICacheObjectDeserializer.cs * * ICacheObjectDeserialzer.cs is part of VNLib.Data.Caching which is part * of the larger VNLib collection of libraries and utilities. @@ -29,7 +29,7 @@ namespace VNLib.Data.Caching /// <summary> /// Provides custom binary deserialzation for a given type /// </summary> - public interface ICacheObjectDeserialzer + public interface ICacheObjectDeserializer { /// <summary> /// Attempts to deserialze the supplied binary buffer to its original @@ -38,6 +38,6 @@ namespace VNLib.Data.Caching /// <param name="objectData">The buffer containing data to deserialze</param> /// <typeparam name="T"></typeparam> /// <returns>A new instance deserialzed to contain the original entity state</returns> - T? Deserialze<T>(ReadOnlySpan<byte> objectData); + T? Deserialize<T>(ReadOnlySpan<byte> objectData); } } diff --git a/lib/VNLib.Data.Caching/src/ICacheObjectSerialzer.cs b/lib/VNLib.Data.Caching/src/ICacheObjectSerializer.cs index fba2df0..658f63e 100644 --- a/lib/VNLib.Data.Caching/src/ICacheObjectSerialzer.cs +++ b/lib/VNLib.Data.Caching/src/ICacheObjectSerializer.cs @@ -3,9 +3,9 @@ * * Library: VNLib * Package: VNLib.Data.Caching -* File: ICacheObjectSerialzer.cs +* File: ICacheObjectSerializer.cs * -* ICacheObjectSerialzer.cs is part of VNLib.Data.Caching which is part +* ICacheObjectSerializer.cs is part of VNLib.Data.Caching which is part * of the larger VNLib collection of libraries and utilities. * * VNLib.Data.Caching is free software: you can redistribute it and/or modify @@ -30,7 +30,7 @@ namespace VNLib.Data.Caching /// <summary> /// Provides custom binary deserialziation for a given type /// </summary> - public interface ICacheObjectSerialzer + public interface ICacheObjectSerializer { /// <summary> /// Serializes an instance of the given type and writes diff --git a/lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs b/lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs index 8a857d4..18c8a98 100644 --- a/lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs +++ b/lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs @@ -22,11 +22,30 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ +using System; using System.Threading; using System.Threading.Tasks; namespace VNLib.Data.Caching { + + /// <summary> + /// A delegate method that will set the raw object data on the state object + /// if data was found + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="state">The state passed to the original call</param> + /// <param name="objectData">The raw data of the cached object</param> + 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 + /// </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); + /// <summary> /// A global cache provider interface /// </summary> @@ -38,6 +57,12 @@ namespace VNLib.Data.Caching bool IsConnected { get; } /// <summary> + /// Gets the underlying cache store object + /// </summary> + /// <returns>The underlying cache store instance</returns> + object GetUnderlyingStore(); + + /// <summary> /// Asynchronously gets a value from the backing cache store /// </summary> /// <typeparam name="T"></typeparam> @@ -63,7 +88,7 @@ namespace VNLib.Data.Caching /// <param name="key">The key identifying the item to delete</param> /// <param name="cancellation">A token to cancel the async operation</param> /// <returns>A task that completes when the delete operation has compelted</returns> - Task DeleteAsync(string key, CancellationToken cancellation); + Task<bool> DeleteAsync(string key, CancellationToken cancellation); /// <summary> /// Asynchronously gets a value from the backing cache store @@ -73,7 +98,7 @@ namespace VNLib.Data.Caching /// <param name="deserializer">The specific deserialzer to deserialze the object</param> /// <param name="cancellation">A token to cancel the async operation</param> /// <returns>The value if found, or null if it does not exist in the store</returns> - Task<T?> GetAsync<T>(string key, ICacheObjectDeserialzer deserializer, CancellationToken cancellation); + Task<T?> GetAsync<T>(string key, ICacheObjectDeserializer deserializer, CancellationToken cancellation); /// <summary> /// Asynchronously sets (or updates) a cached value in the backing cache store @@ -83,19 +108,20 @@ namespace VNLib.Data.Caching /// <param name="newKey">An optional key that will be changed for the new object</param> /// <param name="cancellation">A token to cancel the async operation</param> /// <param name="value">The value to set at the given key</param> - /// <param name="serialzer">The <see cref="ICacheObjectSerialzer"/> used to serialze the entity</param> + /// <param name="serialzer">The <see cref="ICacheObjectSerializer"/> used to serialze the entity</param> /// <returns>A task that completes when the update operation has compelted</returns> - Task AddOrUpdateAsync<T>(string key, string? newKey, T value, ICacheObjectSerialzer serialzer, CancellationToken cancellation); + Task AddOrUpdateAsync<T>(string key, string? newKey, T value, ICacheObjectSerializer serialzer, CancellationToken cancellation); /// <summary> /// Asynchronously gets a value from the backing cache store and writes it to the /// supplied data buffer /// </summary> /// <param name="key">The key identifying the object to recover from cache</param> - /// <param name="rawData">The </param> + /// <param name="callback">The callback method that will get the raw object data</param> + /// <param name="state">The state parameter to pass to the callback when invoked</param> /// <param name="cancellation">A token to cancel the async operation</param> /// <returns>A task that complets when the object data has been written to the data buffer</returns> - Task GetAsync(string key, IObjectData rawData, CancellationToken cancellation); + Task GetAsync<T>(string key, ObjectDataSet<T> callback, T state, CancellationToken cancellation); /// <summary> /// Asynchronously sets (or updates) a cached value in the backing cache store @@ -103,9 +129,10 @@ namespace VNLib.Data.Caching /// </summary> /// <param name="key">The key identifying the object to recover from cache</param> /// <param name="newKey">An optional key that will be changed for the new object</param> + /// <param name="callback">A callback method that will set the raw object data when received</param> + /// <param name="state">The callback state parameter</param> /// <param name="cancellation">A token to cancel the async operation</param> - /// <param name="rawData">The raw data to store at the given key</param> /// <returns>A task that completes when the update operation has compelted</returns> - Task AddOrUpdateAsync(string key, string? newKey, IObjectData rawData, CancellationToken cancellation); + Task AddOrUpdateAsync<T>(string key, string? newKey, ObjectDataReader<T> callback, T state, CancellationToken cancellation); } }
\ No newline at end of file diff --git a/lib/VNLib.Data.Caching/src/JsonCacheObjectSerializer.cs b/lib/VNLib.Data.Caching/src/JsonCacheObjectSerializer.cs index dce0bdf..85d1184 100644 --- a/lib/VNLib.Data.Caching/src/JsonCacheObjectSerializer.cs +++ b/lib/VNLib.Data.Caching/src/JsonCacheObjectSerializer.cs @@ -32,10 +32,10 @@ using VNLib.Utils.Memory.Caching; namespace VNLib.Data.Caching { /// <summary> - /// Implements a <see cref="ICacheObjectDeserialzer"/> and a <see cref="ICacheObjectSerialzer"/> + /// Implements a <see cref="ICacheObjectDeserializer"/> and a <see cref="ICacheObjectSerializer"/> /// that uses JSON serialization, with writer pooling. Members of this class are thread-safe. /// </summary> - public class JsonCacheObjectSerializer : ICacheObjectSerialzer, ICacheObjectDeserialzer + public class JsonCacheObjectSerializer : ICacheObjectSerializer, ICacheObjectDeserializer { //Create threadlocal writer for attempted lock-free writer reuse private static readonly ObjectRental<ReusableJsonWriter> JsonWriterPool = ObjectRental.CreateThreadLocal<ReusableJsonWriter>(); @@ -75,7 +75,7 @@ namespace VNLib.Data.Caching } ///<inheritdoc/> - public virtual T? Deserialze<T>(ReadOnlySpan<byte> objectData) => JsonSerializer.Deserialize<T>(objectData, _options); + public virtual T? Deserialize<T>(ReadOnlySpan<byte> objectData) => JsonSerializer.Deserialize<T>(objectData, _options); ///<inheritdoc/> public virtual void Serialize<T>(T obj, IBufferWriter<byte> finiteWriter) diff --git a/lib/VNLib.Data.Caching/src/WaitForChangeResult.cs b/lib/VNLib.Data.Caching/src/WaitForChangeResult.cs index fe4f22f..18428ce 100644 --- a/lib/VNLib.Data.Caching/src/WaitForChangeResult.cs +++ b/lib/VNLib.Data.Caching/src/WaitForChangeResult.cs @@ -27,14 +27,21 @@ namespace VNLib.Data.Caching /// <summary> /// The result of a cache server change event /// </summary> - /// <param name="Status">The operation status code</param> - /// <param name="CurrentId">The current (or old) id of the element that changed</param> - /// <param name="NewId">The new id of the element that changed</param> - public readonly record struct WaitForChangeResult( - string Status, - string CurrentId, - string NewId) + public sealed record class WaitForChangeResult { - + /// <summary> + /// The operation status code + /// </summary> + public string? Status { get; set; } + + /// <summary> + /// The current (or old) id of the element that changed + /// </summary> + public string? CurrentId { get; set; } + + /// <summary> + /// The new id of the element that changed + /// </summary> + public string? NewId { get; set; } } } |