diff options
author | vnugent <public@vaughnnugent.com> | 2023-03-06 01:53:33 -0500 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-03-06 01:53:33 -0500 |
commit | fd7c37ac02ea56b4f791dd175c1d315b4c682c91 (patch) | |
tree | a0a180630c4c9367726c681bdf0473b24a600a97 /lib/VNLib.Data.Caching/src | |
parent | 7e62ab2caab7c071d0e40df08dc053cc33c6e019 (diff) |
FBM and caching non-breaking updates
Diffstat (limited to 'lib/VNLib.Data.Caching/src')
-rw-r--r-- | lib/VNLib.Data.Caching/src/ClientExtensions.cs | 247 | ||||
-rw-r--r-- | lib/VNLib.Data.Caching/src/ICacheObjectDeserialzer.cs | 43 | ||||
-rw-r--r-- | lib/VNLib.Data.Caching/src/ICacheObjectSerialzer.cs | 44 | ||||
-rw-r--r-- | lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs | 24 | ||||
-rw-r--r-- | lib/VNLib.Data.Caching/src/IObjectData.cs | 46 | ||||
-rw-r--r-- | lib/VNLib.Data.Caching/src/JsonCacheObjectSerializer.cs | 100 | ||||
-rw-r--r-- | lib/VNLib.Data.Caching/src/VNLib.Data.Caching.csproj | 25 |
7 files changed, 475 insertions, 54 deletions
diff --git a/lib/VNLib.Data.Caching/src/ClientExtensions.cs b/lib/VNLib.Data.Caching/src/ClientExtensions.cs index d79735e..1920398 100644 --- a/lib/VNLib.Data.Caching/src/ClientExtensions.cs +++ b/lib/VNLib.Data.Caching/src/ClientExtensions.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Data.Caching @@ -29,11 +29,9 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; -using System.Text.Json.Serialization; using System.Runtime.CompilerServices; using VNLib.Utils.Logging; -using VNLib.Utils.Memory.Caching; using VNLib.Net.Messaging.FBM; using VNLib.Net.Messaging.FBM.Client; using VNLib.Net.Messaging.FBM.Server; @@ -42,28 +40,14 @@ using static VNLib.Data.Caching.Constants; namespace VNLib.Data.Caching { + /// <summary> /// Provides caching extension methods for <see cref="FBMClient"/> /// </summary> public static class ClientExtensions { - //Create threadlocal writer for attempted reuse without locks - private static readonly ObjectRental<ReusableJsonWriter> JsonWriterPool = ObjectRental.Create<ReusableJsonWriter>(); - private static readonly JsonSerializerOptions LocalOptions = new() - { - DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, - NumberHandling = JsonNumberHandling.Strict, - ReadCommentHandling = JsonCommentHandling.Disallow, - WriteIndented = false, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - IgnoreReadOnlyFields = true, - PropertyNameCaseInsensitive = true, - IncludeFields = false, - - //Use small buffers - DefaultBufferSize = 128 - }; + private static readonly JsonCacheObjectSerializer DefaultSerializer = new(); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void LogDebug(this FBMClient client, string message, params object?[] args) @@ -72,7 +56,8 @@ namespace VNLib.Data.Caching } /// <summary> - /// Gets an object from the server if it exists + /// 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> @@ -84,35 +69,77 @@ namespace VNLib.Data.Caching /// <exception cref="InvalidStatusException"></exception> /// <exception cref="ObjectDisposedException"></exception> /// <exception cref="InvalidResponseException"></exception> - public static async Task<T?> GetObjectAsync<T>(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> + /// 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="cancellationToken">A token to cancel the operation</param> + /// <returns>A task that resolves when the server responds</returns> + /// <exception cref="JsonException"></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) + { + //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 + /// </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, ICacheObjectDeserialzer 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 JsonSerializer.Deserialize<T>(response.ResponseBody, LocalOptions); + return (T?)deserialzer.Deserialze(typeof(T), response.ResponseBody); } - + //Object may not exist on the server yet if (status.Value.Equals(ResponseCodes.NotFound, StringComparison.Ordinal)) { @@ -136,23 +163,27 @@ 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="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, T data, CancellationToken cancellationToken = default) + public static async Task AddOrUpdateObjectAsync<T>( + this FBMClient client, + string objectId, + string? newId, + T data, + ICacheObjectSerialzer serializer, + CancellationToken cancellationToken = default) { _ = client ?? throw new ArgumentNullException(nameof(client)); - - client.LogDebug("Updating object {id}, newid {nid}", objectId, newId); + _ = serializer ?? throw new ArgumentNullException(nameof(serializer)); - //Rent new json writer - ReusableJsonWriter writer = JsonWriterPool.Rent(); + client.LogDebug("Updating object {id}, newid {nid}", objectId, newId); //Rent a new request FBMRequest request = client.RentRequest(); @@ -160,7 +191,7 @@ namespace VNLib.Data.Caching { //Set action as get/create request.WriteHeader(HeaderCommand.Action, Actions.AddOrUpdate); - + //Set session-id header request.WriteHeader(Constants.ObjectId, objectId); @@ -169,30 +200,30 @@ namespace VNLib.Data.Caching { request.WriteHeader(Constants.NewObjectId, newId); } - + //Get the body writer for the message IBufferWriter<byte> bodyWriter = request.GetBodyWriter(); //Serialize the message - writer.Serialize(bodyWriter, data, LocalOptions); + serializer.Serialize(data, bodyWriter); //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)) + 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()); } @@ -200,11 +231,9 @@ namespace VNLib.Data.Caching { //Return the request(clears data and reset) client.ReturnRequest(request); - //Return writer to pool later - JsonWriterPool.Return(writer); } } - + /// <summary> /// Asynchronously deletes an object in the remote store /// </summary> @@ -256,6 +285,135 @@ 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 + /// </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">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> + /// <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) + { + _ = client ?? throw new ArgumentNullException(nameof(client)); + _ = data ?? throw new ArgumentNullException(nameof(data)); + + 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 session-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(); + + //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); + } + } + + /// <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="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> + /// <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) + { + _ = client ?? throw new ArgumentNullException(nameof(client)); + _ = data ?? throw new ArgumentNullException(nameof(data)); + + 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)) + { + //Write the object data + data.SetData(response.ResponseBody); + return; + } + + //Object may not exist on the server yet + if (status.Value.Equals(ResponseCodes.NotFound, StringComparison.Ordinal)) + { + return; + } + + throw new InvalidStatusException("Invalid status code recived for object get request", status.ToString()); + } + finally + { + client.ReturnRequest(request); + } + } + + /// <summary> /// Dequeues a change event from the server event queue for the current connection, or waits until a change happens /// </summary> /// <param name="client"></param> @@ -346,5 +504,6 @@ namespace VNLib.Data.Caching //Return new manager return new (worker, retryDelay, serverUri); } + } } diff --git a/lib/VNLib.Data.Caching/src/ICacheObjectDeserialzer.cs b/lib/VNLib.Data.Caching/src/ICacheObjectDeserialzer.cs new file mode 100644 index 0000000..ec3fdb6 --- /dev/null +++ b/lib/VNLib.Data.Caching/src/ICacheObjectDeserialzer.cs @@ -0,0 +1,43 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Data.Caching +* File: ICacheObjectDeserialzer.cs +* +* ICacheObjectDeserialzer.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; + +namespace VNLib.Data.Caching +{ + /// <summary> + /// Provides custom binary deserialzation for a given type + /// </summary> + public interface ICacheObjectDeserialzer + { + /// <summary> + /// Attempts to deserialze the supplied binary buffer to its original + /// object state. + /// </summary> + /// <param name="objectData">The buffer containing data to deserialze</param> + /// <param name="type">The type to deserialze</param> + /// <returns>A new instance deserialzed to contain the original entity state</returns> + object? Deserialze(Type type, ReadOnlySpan<byte> objectData); + } +} diff --git a/lib/VNLib.Data.Caching/src/ICacheObjectSerialzer.cs b/lib/VNLib.Data.Caching/src/ICacheObjectSerialzer.cs new file mode 100644 index 0000000..fba2df0 --- /dev/null +++ b/lib/VNLib.Data.Caching/src/ICacheObjectSerialzer.cs @@ -0,0 +1,44 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Data.Caching +* File: ICacheObjectSerialzer.cs +* +* ICacheObjectSerialzer.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.Buffers; + +namespace VNLib.Data.Caching +{ + /// <summary> + /// Provides custom binary deserialziation for a given type + /// </summary> + public interface ICacheObjectSerialzer + { + /// <summary> + /// Serializes an instance of the given type and writes + /// the output to the supplied buffer writer + /// </summary> + /// <typeparam name="T">The type to serialze</typeparam> + /// <param name="obj">The object instance to serialize</param> + /// <param name="finiteWriter">A finite sized buffer writer to commit the serialized data to</param> + void Serialize<T>(T obj, IBufferWriter<byte> finiteWriter); + } +} diff --git a/lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs b/lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs index eeee9e2..5880192 100644 --- a/lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs +++ b/lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Data.Caching @@ -64,5 +64,27 @@ namespace VNLib.Data.Caching /// <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); + + /// <summary> + /// Asynchronously gets a value from the backing cache store + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="key">The key identifying the object to recover from cache</param> + /// <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); + + /// <summary> + /// Asynchronously sets (or updates) a cached value in the backing cache store + /// </summary> + /// <typeparam name="T"></typeparam> + /// <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="value">The value to set at the given key</param> + /// <param name="serialzer">The <see cref="ICacheObjectSerialzer{T}"/> 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); } }
\ No newline at end of file diff --git a/lib/VNLib.Data.Caching/src/IObjectData.cs b/lib/VNLib.Data.Caching/src/IObjectData.cs new file mode 100644 index 0000000..e18de39 --- /dev/null +++ b/lib/VNLib.Data.Caching/src/IObjectData.cs @@ -0,0 +1,46 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Data.Caching +* File: IObjectData.cs +* +* IObjectData.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; + +namespace VNLib.Data.Caching +{ + /// <summary> + /// Represents object data to send or receive from a cache server + /// </summary> + public interface IObjectData + { + /// <summary> + /// Gets the cachable objects data to store in the remote cache server + /// </summary> + /// <returns>The object data to set</returns> + ReadOnlySpan<byte> GetData(); + + /// <summary> + /// Stores the retrieved object data from the remote server + /// </summary> + /// <param name="data">The object data found on the remote server</param> + void SetData(ReadOnlySpan<byte> data); + } +} diff --git a/lib/VNLib.Data.Caching/src/JsonCacheObjectSerializer.cs b/lib/VNLib.Data.Caching/src/JsonCacheObjectSerializer.cs new file mode 100644 index 0000000..7f52169 --- /dev/null +++ b/lib/VNLib.Data.Caching/src/JsonCacheObjectSerializer.cs @@ -0,0 +1,100 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Data.Caching +* File: JsonCacheObjectSerializer.cs +* +* JsonCacheObjectSerializer.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.Buffers; +using System.Text.Json; +using System.Text.Json.Serialization; + +using VNLib.Utils.Memory.Caching; + +namespace VNLib.Data.Caching +{ + /// <summary> + /// Implements a <see cref="ICacheObjectDeserialzer"/> and a <see cref="ICacheObjectSerialzer"/> + /// that uses JSON serialization, with writer pooling. Members of this class are thread-safe. + /// </summary> + public class JsonCacheObjectSerializer : ICacheObjectSerialzer, ICacheObjectDeserialzer + { + //Create threadlocal writer for attempted lock-free writer reuse + private static readonly ObjectRental<ReusableJsonWriter> JsonWriterPool = ObjectRental.CreateThreadLocal<ReusableJsonWriter>(); + + private readonly JsonSerializerOptions? _options; + + /// <summary> + /// Initializes a new <see cref="JsonCacheObjectSerializer"/> + /// </summary> + /// <param name="options">JSON serialization/deserialization options</param> + public JsonCacheObjectSerializer(JsonSerializerOptions options) + { + _options = options; + } + + /// <summary> + /// Initializes a new <see cref="JsonCacheObjectSerializer"/> using + /// the default serialization rules + /// </summary> + public JsonCacheObjectSerializer() + { + //Configure default serialzation options + _options = new() + { + DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, + NumberHandling = JsonNumberHandling.Strict, + ReadCommentHandling = JsonCommentHandling.Disallow, + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + IgnoreReadOnlyFields = true, + PropertyNameCaseInsensitive = true, + IncludeFields = false, + + //Use small buffers + DefaultBufferSize = 128 + }; + } + + ///<inheritdoc/> + public virtual object? Deserialze(Type type, ReadOnlySpan<byte> objectData) + { + return JsonSerializer.Deserialize(objectData, type, _options); + } + + ///<inheritdoc/> + public virtual void Serialize<T>(T obj, IBufferWriter<byte> finiteWriter) + { + //Rent new json writer + ReusableJsonWriter writer = JsonWriterPool.Rent(); + + try + { + //Serialize the message + writer.Serialize(finiteWriter, obj, _options); + } + finally + { + JsonWriterPool.Return(writer); + } + } + } +} diff --git a/lib/VNLib.Data.Caching/src/VNLib.Data.Caching.csproj b/lib/VNLib.Data.Caching/src/VNLib.Data.Caching.csproj index bf99561..6be9884 100644 --- a/lib/VNLib.Data.Caching/src/VNLib.Data.Caching.csproj +++ b/lib/VNLib.Data.Caching/src/VNLib.Data.Caching.csproj @@ -2,18 +2,25 @@ <PropertyGroup> <TargetFramework>net6.0</TargetFramework> - <Authors>Vaughn Nugent</Authors> - <Copyright>Copyright © 2023 Vaughn Nugent</Copyright> - <Version>1.0.1.1</Version> - <GenerateDocumentationFile>True</GenerateDocumentationFile> + <RootNamespace>VNLib.Data.Caching</RootNamespace> + <AssemblyName>VNLib.Data.Caching</AssemblyName> <Nullable>enable</Nullable> - <DocumentationFile></DocumentationFile> + <GenerateDocumentationFile>True</GenerateDocumentationFile> <AnalysisLevel>latest-all</AnalysisLevel> <PackageReadmeFile>README.md</PackageReadmeFile> - <Description>Provides constants and extensions for a key-value data caching protocol layer atop the Fixed Buffer Messaging protocol to work VNCache servers. Provides rapid cache development</Description> - <PackageProjectUrl>https://www.vaughnnugent.com/resources/software</PackageProjectUrl> - <SignAssembly>True</SignAssembly> - <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> + </PropertyGroup> + + <PropertyGroup> + <PackageId>VNLib.Data.Caching</PackageId> + <Authors>Vaughn Nugent</Authors> + <Company>Vaughn Nugent</Company> + <Product>VNLib FBM Data caching extension library</Product> + <Description> + Provides constants and extensions for a key-value data caching protocol layer atop the Fixed Buffer Messaging protocol to work VNCache servers. Provides rapid cache development + </Description> + <Copyright>Copyright © 2023 Vaughn Nugent</Copyright> + <PackageProjectUrl>https://www.vaughnnugent.com/resources/software/modules/VNLib.Data.Caching</PackageProjectUrl> + <RepositoryUrl>https://github.com/VnUgE/VNLib.Data.Caching/tree/master/lib/VNLib.Data.Caching</RepositoryUrl> </PropertyGroup> <ItemGroup> |