aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-03-06 01:53:33 -0500
committerLibravatar vnugent <public@vaughnnugent.com>2023-03-06 01:53:33 -0500
commitfd7c37ac02ea56b4f791dd175c1d315b4c682c91 (patch)
treea0a180630c4c9367726c681bdf0473b24a600a97
parent7e62ab2caab7c071d0e40df08dc053cc33c6e019 (diff)
FBM and caching non-breaking updates
-rw-r--r--lib/VNLib.Data.Caching.Extensions/src/FBMDataCacheExtensions.cs7
-rw-r--r--lib/VNLib.Data.Caching.Extensions/src/VNLib.Data.Caching.Extensions.csproj23
-rw-r--r--lib/VNLib.Data.Caching/src/ClientExtensions.cs247
-rw-r--r--lib/VNLib.Data.Caching/src/ICacheObjectDeserialzer.cs43
-rw-r--r--lib/VNLib.Data.Caching/src/ICacheObjectSerialzer.cs44
-rw-r--r--lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs24
-rw-r--r--lib/VNLib.Data.Caching/src/IObjectData.cs46
-rw-r--r--lib/VNLib.Data.Caching/src/JsonCacheObjectSerializer.cs100
-rw-r--r--lib/VNLib.Data.Caching/src/VNLib.Data.Caching.csproj25
9 files changed, 496 insertions, 63 deletions
diff --git a/lib/VNLib.Data.Caching.Extensions/src/FBMDataCacheExtensions.cs b/lib/VNLib.Data.Caching.Extensions/src/FBMDataCacheExtensions.cs
index 222240a..723af29 100644
--- a/lib/VNLib.Data.Caching.Extensions/src/FBMDataCacheExtensions.cs
+++ b/lib/VNLib.Data.Caching.Extensions/src/FBMDataCacheExtensions.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Data.Caching.Extensions
@@ -78,8 +78,9 @@ namespace VNLib.Data.Caching.Extensions
/// <param name="heap">The client buffer heap</param>
/// <param name="maxMessageSize">The maxium message size (in bytes)</param>
/// <param name="debugLog">An optional debug log</param>
+ /// <param name="timeout">Request message timeout</param>
/// <returns>A preconfigured <see cref="FBMClientConfig"/> for object caching</returns>
- public static FBMClientConfig GetDefaultConfig(IUnmangedHeap heap, int maxMessageSize, ILogProvider? debugLog = null)
+ public static FBMClientConfig GetDefaultConfig(IUnmangedHeap heap, int maxMessageSize, TimeSpan timeout = default, ILogProvider? debugLog = null)
{
/*
* Max message size (for server) should account for max data + the additional header buffer
@@ -109,6 +110,8 @@ namespace VNLib.Data.Caching.Extensions
KeepAliveInterval = TimeSpan.FromSeconds(30),
+ RequestTimeout = timeout,
+
DebugLog = debugLog
};
}
diff --git a/lib/VNLib.Data.Caching.Extensions/src/VNLib.Data.Caching.Extensions.csproj b/lib/VNLib.Data.Caching.Extensions/src/VNLib.Data.Caching.Extensions.csproj
index 5792243..ead53bb 100644
--- a/lib/VNLib.Data.Caching.Extensions/src/VNLib.Data.Caching.Extensions.csproj
+++ b/lib/VNLib.Data.Caching.Extensions/src/VNLib.Data.Caching.Extensions.csproj
@@ -2,19 +2,28 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
- <ImplicitUsings>enable</ImplicitUsings>
+ <RootNamespace>VNLib.Data.Caching.Extensions</RootNamespace>
+ <AssemblyName>VNLib.Data.Caching.Extensions</AssemblyName>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
- <Version>1.0.1.1</Version>
+ <AnalysisLevel>latest-all</AnalysisLevel>
+ <PackageReadmeFile>README.md</PackageReadmeFile>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <PackageId>VNLib.Data.Caching.Extensions</PackageId>
<Authors>Vaughn Nugent</Authors>
+ <Company>Vaughn Nugent</Company>
+ <Product>VNLib distributed caching extension library</Product>
+ <Description>
+ A libray for working with VNCache distributed object cache networks.
+ </Description>
<Copyright>Copyright © 2023 Vaughn Nugent</Copyright>
- <PackageProjectUrl>https://www.vaughnnugent.com/resources</PackageProjectUrl>
- <AnalysisLevel>latest-all</AnalysisLevel>
- <Description>A libray for working with VNCache object cache clusters. </Description>
- <SignAssembly>True</SignAssembly>
- <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile>
+ <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.Extensions</RepositoryUrl>
</PropertyGroup>
+
<ItemGroup>
<PackageReference Include="ErrorProne.NET.CoreAnalyzers" Version="0.1.2">
<PrivateAssets>all</PrivateAssets>
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>