aboutsummaryrefslogtreecommitdiff
path: root/lib/VNLib.Data.Caching/src
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-01-12 17:47:40 -0500
committerLibravatar vnugent <public@vaughnnugent.com>2023-01-12 17:47:40 -0500
commitb75668b164d398b99ee942beced06aa27ef65a50 (patch)
treec1faf6df3caa78083dcc38eb1a7247e456bbe754 /lib/VNLib.Data.Caching/src
parentcea64e619e714f6dbe51d37ca8329b58d8c271fb (diff)
Large project reorder and consolidation
Diffstat (limited to 'lib/VNLib.Data.Caching/src')
-rw-r--r--lib/VNLib.Data.Caching/src/ClientExtensions.cs328
-rw-r--r--lib/VNLib.Data.Caching/src/ClientRetryManager.cs107
-rw-r--r--lib/VNLib.Data.Caching/src/Constants.cs56
-rw-r--r--lib/VNLib.Data.Caching/src/Exceptions/InvalidStatusException.cs63
-rw-r--r--lib/VNLib.Data.Caching/src/Exceptions/MessageTooLargeException.cs50
-rw-r--r--lib/VNLib.Data.Caching/src/Exceptions/ObjectNotFoundException.cs47
-rw-r--r--lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs68
-rw-r--r--lib/VNLib.Data.Caching/src/VNLib.Data.Caching.csproj46
-rw-r--r--lib/VNLib.Data.Caching/src/WaitForChangeResult.cs45
9 files changed, 810 insertions, 0 deletions
diff --git a/lib/VNLib.Data.Caching/src/ClientExtensions.cs b/lib/VNLib.Data.Caching/src/ClientExtensions.cs
new file mode 100644
index 0000000..a207886
--- /dev/null
+++ b/lib/VNLib.Data.Caching/src/ClientExtensions.cs
@@ -0,0 +1,328 @@
+/*
+* Copyright (c) 2022 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Data.Caching
+* File: ClientExtensions.cs
+*
+* ClientExtensions.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.IO;
+using System.Linq;
+using System.Buffers;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+using VNLib.Utils.Logging;
+using VNLib.Net.Messaging.FBM;
+using VNLib.Net.Messaging.FBM.Client;
+using VNLib.Net.Messaging.FBM.Server;
+using VNLib.Data.Caching.Exceptions;
+
+using static VNLib.Data.Caching.Constants;
+
+namespace VNLib.Data.Caching
+{
+
+ /// <summary>
+ /// Provides caching extension methods for <see cref="FBMClient"/>
+ /// </summary>
+ public static class ClientExtensions
+ {
+ 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 void LogDebug(this FBMClient client, string message, params object?[] args)
+ {
+ if (client.Config.DebugLog != null)
+ {
+ client.Config.DebugLog.Debug($"[CACHE] : {message}", args);
+ }
+ }
+
+ /// <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>
+ /// <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 async Task<T?> GetObjectAsync<T>(this FBMClient client, string objectId, CancellationToken cancellationToken = default)
+ {
+ _ = client ?? throw new ArgumentNullException(nameof(client));
+
+ 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 session-id header
+ request.WriteHeader(Constants.ObjectId, objectId);
+
+ //Make request
+ using FBMResponse response = await client.SendAsync(request, cancellationToken);
+
+ response.ThrowIfNotSet();
+ //Get the status code
+ ReadOnlyMemory<char> status = response.Headers.FirstOrDefault(static a => a.Key == HeaderCommand.Status).Value;
+ if (status.Span.Equals(ResponseCodes.Okay, StringComparison.Ordinal))
+ {
+ return JsonSerializer.Deserialize<T>(response.ResponseBody, LocalOptions);
+ }
+ //Session may not exist on the server yet
+ if (status.Span.Equals(ResponseCodes.NotFound, StringComparison.Ordinal))
+ {
+ return default;
+ }
+ throw new InvalidStatusException("Invalid status code recived for object get request", status.ToString());
+ }
+ finally
+ {
+ client.ReturnRequest(request);
+ }
+ }
+
+ /// <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 async Task AddOrUpdateObjectAsync<T>(this FBMClient client, string objectId, string? newId, T data, CancellationToken cancellationToken = default)
+ {
+ _ = client ?? throw new ArgumentNullException(nameof(client));
+
+ 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);
+ }
+ //Get the body writer for the message
+ IBufferWriter<byte> bodyWriter = request.GetBodyWriter();
+ //Write json data to the message
+ using (Utf8JsonWriter jsonWriter = new(bodyWriter))
+ {
+ JsonSerializer.Serialize(jsonWriter, data, LocalOptions);
+ }
+
+ //Make request
+ using FBMResponse response = await client.SendAsync(request, cancellationToken);
+
+ response.ThrowIfNotSet();
+ //Get the status code
+ ReadOnlyMemory<char> status = response.Headers.FirstOrDefault(static a => a.Key == HeaderCommand.Status).Value;
+ //Check status code
+ if (status.Span.Equals(ResponseCodes.Okay, StringComparison.OrdinalIgnoreCase))
+ {
+ return;
+ }
+ else if(status.Span.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>
+ /// Asynchronously deletes an object in the remote store
+ /// </summary>
+ /// <param name="client"></param>
+ /// <param name="objectId">The id of the object to update or replace</param>
+ /// <param name="cancellationToken">A token to cancel the operation</param>
+ /// <returns>A task that resolves when the operation has completed</returns>
+ /// <exception cref="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)
+ {
+ _ = client ?? throw new ArgumentNullException(nameof(client));
+
+ 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
+ request.WriteHeader(Constants.ObjectId, objectId);
+
+ //Make request
+ using FBMResponse response = await client.SendAsync(request, cancellationToken);
+
+ response.ThrowIfNotSet();
+ //Get the status code
+ ReadOnlyMemory<char> status = response.Headers.FirstOrDefault(static a => a.Key == HeaderCommand.Status).Value;
+ if (status.Span.Equals(ResponseCodes.Okay, StringComparison.Ordinal))
+ {
+ return;
+ }
+ else if(status.Span.Equals(ResponseCodes.NotFound, StringComparison.OrdinalIgnoreCase))
+ {
+ throw new ObjectNotFoundException($"object {objectId} not found on remote server");
+ }
+ 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>
+ /// <param name="cancellationToken">A token to cancel the deuque operation</param>
+ /// <returns>A <see cref="KeyValuePair{TKey, TValue}"/> that contains the modified object id and optionally its new id</returns>
+ public static async Task<WaitForChangeResult> WaitForChangeAsync(this FBMClient client, CancellationToken cancellationToken = default)
+ {
+ //Rent a new request
+ FBMRequest request = client.RentRequest();
+ try
+ {
+ //Set action as event dequeue to dequeue a change event
+ request.WriteHeader(HeaderCommand.Action, Actions.Dequeue);
+
+ //Make request
+ using FBMResponse response = await client.SendAsync(request, cancellationToken);
+
+ response.ThrowIfNotSet();
+
+ return new()
+ {
+ Status = response.Headers.FirstOrDefault(static a => a.Key == HeaderCommand.Status).Value.ToString(),
+ CurrentId = response.Headers.SingleOrDefault(static v => v.Key == Constants.ObjectId).Value.ToString(),
+ NewId = response.Headers.SingleOrDefault(static v => v.Key == Constants.NewObjectId).Value.ToString()
+ };
+ }
+ finally
+ {
+ client.ReturnRequest(request);
+ }
+ }
+
+ /// <summary>
+ /// Gets the Object-id for the request message, or throws an <see cref="InvalidOperationException"/> if not specified
+ /// </summary>
+ /// <param name="context"></param>
+ /// <returns>The id of the object requested</returns>
+ /// <exception cref="InvalidOperationException"></exception>
+ public static string ObjectId(this FBMContext context)
+ {
+ return context.Request.Headers.First(static kvp => kvp.Key == Constants.ObjectId).Value.ToString();
+ }
+ /// <summary>
+ /// Gets the new ID of the object if specified from the request. Null if the request did not specify an id update
+ /// </summary>
+ /// <param name="context"></param>
+ /// <returns>The new ID of the object if speicifed, null otherwise</returns>
+ public static string? NewObjectId(this FBMContext context)
+ {
+ return context.Request.Headers.FirstOrDefault(static kvp => kvp.Key == Constants.NewObjectId).Value.ToString();
+ }
+ /// <summary>
+ /// Gets the request method for the request
+ /// </summary>
+ /// <param name="context"></param>
+ /// <returns>The request method string</returns>
+ public static string Method(this FBMContext context)
+ {
+ return context.Request.Headers.First(static kvp => kvp.Key == HeaderCommand.Action).Value.ToString();
+ }
+ /// <summary>
+ /// Closes a response with a status code
+ /// </summary>
+ /// <param name="context"></param>
+ /// <param name="responseCode">The status code to send to the client</param>
+ public static void CloseResponse(this FBMContext context, string responseCode)
+ {
+ context.Response.WriteHeader(HeaderCommand.Status, responseCode);
+ }
+
+
+ /// <summary>
+ /// Initializes the worker for a reconnect policy and returns an object that can listen for changes
+ /// and configure the connection as necessary
+ /// </summary>
+ /// <param name="worker"></param>
+ /// <param name="retryDelay">The amount of time to wait between retries</param>
+ /// <param name="serverUri">The uri to reconnect the client to</param>
+ /// <returns>A <see cref="ClientRetryManager{T}"/> for listening for retry events</returns>
+ public static ClientRetryManager<T> SetReconnectPolicy<T>(this T worker, TimeSpan retryDelay, Uri serverUri) where T: IStatefulConnection
+ {
+ //Return new manager
+ return new (worker, retryDelay, serverUri);
+ }
+ }
+}
diff --git a/lib/VNLib.Data.Caching/src/ClientRetryManager.cs b/lib/VNLib.Data.Caching/src/ClientRetryManager.cs
new file mode 100644
index 0000000..5cee583
--- /dev/null
+++ b/lib/VNLib.Data.Caching/src/ClientRetryManager.cs
@@ -0,0 +1,107 @@
+/*
+* Copyright (c) 2022 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Data.Caching
+* File: ClientRetryManager.cs
+*
+* ClientRetryManager.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.Tasks;
+using System.Security.Cryptography;
+
+using VNLib.Utils;
+using VNLib.Net.Messaging.FBM.Client;
+
+namespace VNLib.Data.Caching
+{
+ /// <summary>
+ /// Manages a <see cref="FBMClientWorkerBase"/> reconnect policy
+ /// </summary>
+ public class ClientRetryManager<T> : VnDisposeable where T: IStatefulConnection
+ {
+ const int RetryRandMaxMsDelay = 1000;
+
+ private readonly TimeSpan RetryDelay;
+ private readonly T Client;
+ private readonly Uri ServerUri;
+
+ internal ClientRetryManager(T worker, TimeSpan delay, Uri serverUri)
+ {
+ this.Client = worker;
+ this.RetryDelay = delay;
+ this.ServerUri = serverUri;
+ //Register disconnect listener
+ worker.ConnectionClosed += Worker_Disconnected;
+ }
+
+ private void Worker_Disconnected(object? sender, EventArgs args)
+ {
+ //Exec retry on exit
+ _ = RetryAsync().ConfigureAwait(false);
+ }
+
+
+ /// <summary>
+ /// Raised before client is to be reconnected
+ /// </summary>
+ public event Action<T>? OnBeforeReconnect;
+ /// <summary>
+ /// Raised when the client fails to reconnect. Should return a value that instructs the
+ /// manager to reconnect
+ /// </summary>
+ public event Func<T, Exception, bool>? OnReconnectFailed;
+
+ async Task RetryAsync()
+ {
+
+ //Begin random delay with retry ms
+ int randomDelayMs = (int)RetryDelay.TotalMilliseconds;
+ //random delay to add to prevent retry-storm
+ randomDelayMs += RandomNumberGenerator.GetInt32(RetryRandMaxMsDelay);
+ //Retry loop
+ bool retry = true;
+ while (retry)
+ {
+ try
+ {
+ //Inform Listener for the retry
+ OnBeforeReconnect?.Invoke(Client);
+ //wait for delay before reconnecting
+ await Task.Delay(randomDelayMs);
+ //Reconnect async
+ await Client.ConnectAsync(ServerUri).ConfigureAwait(false);
+ break;
+ }
+ catch (Exception Ex)
+ {
+ //Invoke error handler, may be null, incase exit
+ retry = OnReconnectFailed?.Invoke(Client, Ex) ?? false;
+ }
+ }
+ }
+
+ ///<inheritdoc/>
+ protected override void Free()
+ {
+ //Unregister the event listener
+ Client.ConnectionClosed -= Worker_Disconnected;
+ }
+ }
+}
diff --git a/lib/VNLib.Data.Caching/src/Constants.cs b/lib/VNLib.Data.Caching/src/Constants.cs
new file mode 100644
index 0000000..3fb87e0
--- /dev/null
+++ b/lib/VNLib.Data.Caching/src/Constants.cs
@@ -0,0 +1,56 @@
+/*
+* Copyright (c) 2022 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Data.Caching
+* File: Constants.cs
+*
+* Constants.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 VNLib.Net.Messaging.FBM;
+
+namespace VNLib.Data.Caching
+{
+ public static class Constants
+ {
+ /// <summary>
+ /// Contains constants the define actions
+ /// </summary>
+ public static class Actions
+ {
+ public const string Get= "g";
+ public const string AddOrUpdate = "u";
+ public const string Delete = "d";
+ public const string Dequeue = "dq";
+ }
+ /// <summary>
+ /// Containts constants for operation response codes
+ /// </summary>
+ public static class ResponseCodes
+ {
+ public const string Okay = "ok";
+ public const string Error = "err";
+ public const string NotFound = "nf";
+ }
+
+ public const HeaderCommand ObjectId = (HeaderCommand)0xAA;
+ public const HeaderCommand NewObjectId = (HeaderCommand)0xAB;
+ }
+}
diff --git a/lib/VNLib.Data.Caching/src/Exceptions/InvalidStatusException.cs b/lib/VNLib.Data.Caching/src/Exceptions/InvalidStatusException.cs
new file mode 100644
index 0000000..2296774
--- /dev/null
+++ b/lib/VNLib.Data.Caching/src/Exceptions/InvalidStatusException.cs
@@ -0,0 +1,63 @@
+/*
+* Copyright (c) 2022 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Data.Caching
+* File: InvalidStatusException.cs
+*
+* InvalidStatusException.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 VNLib.Net.Messaging.FBM;
+
+namespace VNLib.Data.Caching.Exceptions
+{
+ /// <summary>
+ /// Raised when the response status code of an FBM Request message is not valid for
+ /// the specified request
+ /// </summary>
+ public class InvalidStatusException : InvalidResponseException
+ {
+ private readonly string? StatusCode;
+ /// <summary>
+ /// Initalizes a new <see cref="InvalidStatusException"/> with the specfied status code
+ /// </summary>
+ /// <param name="message"></param>
+ /// <param name="statusCode"></param>
+ public InvalidStatusException(string message, string statusCode):this(message)
+ {
+ this.StatusCode = statusCode;
+ }
+
+ ///<inheritdoc/>
+ public InvalidStatusException()
+ {
+ }
+ ///<inheritdoc/>
+ public InvalidStatusException(string message) : base(message)
+ {
+ }
+ ///<inheritdoc/>
+ public InvalidStatusException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+ ///<inheritdoc/>
+ public override string Message => $"InvalidStatusException: Status Code {StatusCode} \r\n {base.Message}";
+ }
+}
diff --git a/lib/VNLib.Data.Caching/src/Exceptions/MessageTooLargeException.cs b/lib/VNLib.Data.Caching/src/Exceptions/MessageTooLargeException.cs
new file mode 100644
index 0000000..c306ba5
--- /dev/null
+++ b/lib/VNLib.Data.Caching/src/Exceptions/MessageTooLargeException.cs
@@ -0,0 +1,50 @@
+/*
+* Copyright (c) 2022 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Data.Caching
+* File: MessageTooLargeException.cs
+*
+* MessageTooLargeException.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.Runtime.Serialization;
+
+using VNLib.Net.Messaging.FBM;
+
+namespace VNLib.Data.Caching.Exceptions
+{
+ /// <summary>
+ /// Raised when a request (or server response) calculates the size of the message to be too large to proccess
+ /// </summary>
+ public class MessageTooLargeException : FBMException
+ {
+ ///<inheritdoc/>
+ public MessageTooLargeException()
+ {}
+ ///<inheritdoc/>
+ public MessageTooLargeException(string message) : base(message)
+ {}
+ ///<inheritdoc/>
+ public MessageTooLargeException(string message, Exception innerException) : base(message, innerException)
+ {}
+ ///<inheritdoc/>
+ protected MessageTooLargeException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {}
+ }
+}
diff --git a/lib/VNLib.Data.Caching/src/Exceptions/ObjectNotFoundException.cs b/lib/VNLib.Data.Caching/src/Exceptions/ObjectNotFoundException.cs
new file mode 100644
index 0000000..e996ad6
--- /dev/null
+++ b/lib/VNLib.Data.Caching/src/Exceptions/ObjectNotFoundException.cs
@@ -0,0 +1,47 @@
+/*
+* Copyright (c) 2022 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Data.Caching
+* File: ObjectNotFoundException.cs
+*
+* ObjectNotFoundException.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.Exceptions
+{
+ /// <summary>
+ /// Raised when a command was executed on a desired object in the remote cache
+ /// but the object was not found
+ /// </summary>
+ public class ObjectNotFoundException : InvalidStatusException
+ {
+ internal ObjectNotFoundException()
+ {}
+
+ internal ObjectNotFoundException(string message) : base(message)
+ {}
+
+ internal ObjectNotFoundException(string message, string statusCode) : base(message, statusCode)
+ {}
+
+ internal ObjectNotFoundException(string message, Exception innerException) : base(message, innerException)
+ {}
+ }
+}
diff --git a/lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs b/lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs
new file mode 100644
index 0000000..eeee9e2
--- /dev/null
+++ b/lib/VNLib.Data.Caching/src/IGlobalCacheProvider.cs
@@ -0,0 +1,68 @@
+/*
+* Copyright (c) 2022 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Data.Caching
+* File: IGlobalCacheProvider.cs
+*
+* IGlobalCacheProvider.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.Threading;
+using System.Threading.Tasks;
+
+namespace VNLib.Data.Caching
+{
+ /// <summary>
+ /// A global cache provider interface
+ /// </summary>
+ public interface IGlobalCacheProvider
+ {
+ /// <summary>
+ /// Gets a value that indicates if the cache provider is currently available
+ /// </summary>
+ bool IsConnected { get; }
+
+ /// <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="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, 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>
+ /// <returns>A task that completes when the update operation has compelted</returns>
+ Task AddOrUpdateAsync<T>(string key, string? newKey, T value, CancellationToken cancellation);
+
+ /// <summary>
+ /// Asynchronously deletes an item from cache by its key
+ /// </summary>
+ /// <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);
+ }
+} \ No newline at end of file
diff --git a/lib/VNLib.Data.Caching/src/VNLib.Data.Caching.csproj b/lib/VNLib.Data.Caching/src/VNLib.Data.Caching.csproj
new file mode 100644
index 0000000..eeaaa11
--- /dev/null
+++ b/lib/VNLib.Data.Caching/src/VNLib.Data.Caching.csproj
@@ -0,0 +1,46 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net6.0</TargetFramework>
+ <Authors>Vaughn Nugent</Authors>
+ <Copyright>Copyright © 2023 Vaughn Nugent</Copyright>
+ <Version>1.0.1.1</Version>
+ <GenerateDocumentationFile>True</GenerateDocumentationFile>
+ <Nullable>enable</Nullable>
+ <DocumentationFile></DocumentationFile>
+ <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>
+ <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
+ <AnalysisLevel>latest-all</AnalysisLevel>
+ <Title>$(AssemblyName)</Title>
+ <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>
+ <PackageReadmeFile>README.md</PackageReadmeFile>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <None Include="..\README.md">
+ <Pack>True</Pack>
+ <PackagePath>\</PackagePath>
+ </None>
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="ErrorProne.NET.CoreAnalyzers" Version="0.1.2">
+ <PrivateAssets>all</PrivateAssets>
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+ </PackageReference>
+ <PackageReference Include="ErrorProne.NET.Structs" Version="0.1.2">
+ <PrivateAssets>all</PrivateAssets>
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+ </PackageReference>
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\..\..\core\lib\Net.Messaging.FBM\src\VNLib.Net.Messaging.FBM.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/lib/VNLib.Data.Caching/src/WaitForChangeResult.cs b/lib/VNLib.Data.Caching/src/WaitForChangeResult.cs
new file mode 100644
index 0000000..76cc002
--- /dev/null
+++ b/lib/VNLib.Data.Caching/src/WaitForChangeResult.cs
@@ -0,0 +1,45 @@
+/*
+* Copyright (c) 2022 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Data.Caching
+* File: WaitForChangeResult.cs
+*
+* WaitForChangeResult.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/.
+*/
+
+namespace VNLib.Data.Caching
+{
+ /// <summary>
+ /// The result of a cache server change event
+ /// </summary>
+ public readonly struct WaitForChangeResult
+ {
+ /// <summary>
+ /// The operation status code
+ /// </summary>
+ public readonly string Status { get; init; }
+ /// <summary>
+ /// The current (or old) id of the element that changed
+ /// </summary>
+ public readonly string CurrentId { get; init; }
+ /// <summary>
+ /// The new id of the element that changed
+ /// </summary>
+ public readonly string NewId { get; init; }
+ }
+}