diff options
Diffstat (limited to 'lib/VNLib.Data.Caching.ObjectCache/src')
6 files changed, 749 insertions, 0 deletions
diff --git a/lib/VNLib.Data.Caching.ObjectCache/src/BlobCache.cs b/lib/VNLib.Data.Caching.ObjectCache/src/BlobCache.cs new file mode 100644 index 0000000..26df351 --- /dev/null +++ b/lib/VNLib.Data.Caching.ObjectCache/src/BlobCache.cs @@ -0,0 +1,139 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Data.Caching.ObjectCache +* File: BlobCache.cs +* +* BlobCache.cs is part of VNLib.Data.Caching.ObjectCache which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Data.Caching.ObjectCache 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.ObjectCache 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.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +using VNLib.Utils.IO; +using VNLib.Utils.Logging; +using VNLib.Utils.Memory; +using VNLib.Utils.Memory.Caching; + +namespace VNLib.Data.Caching +{ + /// <summary> + /// A general purpose binary data storage + /// </summary> + public class BlobCache : LRUCache<string, MemoryHandle<byte>> + { + readonly IUnmangedHeap Heap; + readonly DirectoryInfo SwapDir; + readonly ILogProvider Log; + ///<inheritdoc/> + public override bool IsReadOnly { get; } + ///<inheritdoc/> + protected override int MaxCapacity { get; } + + + /// <summary> + /// Initializes a new <see cref="BlobCache"/> store + /// </summary> + /// <param name="swapDir">The <see cref="IsolatedStorageDirectory"/> to swap blob data to when cache</param> + /// <param name="maxCapacity">The maximum number of items to keep in memory</param> + /// <param name="log">A <see cref="ILogProvider"/> to write log data to</param> + /// <param name="heap">A <see cref="IUnmangedHeap"/> to allocate buffers and store <see cref="BlobItem"/> data in memory</param> + public BlobCache(DirectoryInfo swapDir, int maxCapacity, ILogProvider log, IUnmangedHeap heap) + :base(StringComparer.Ordinal) + { + IsReadOnly = false; + MaxCapacity = maxCapacity; + SwapDir = swapDir; + //Update the lookup table size + LookupTable.EnsureCapacity(maxCapacity); + //Set default heap if not specified + Heap = heap; + Log = log; + } + ///<inheritdoc/> + protected override bool CacheMiss(string key, [NotNullWhen(true)] out MemoryHandle<byte>? value) + { + value = null; + return false; + } + ///<inheritdoc/> + protected override void Evicted(KeyValuePair<string, MemoryHandle<byte>> evicted) + { + //Dispose the blob + evicted.Value.Dispose(); + } + /// <summary> + /// If the <see cref="BlobItem"/> is found in the store, changes the key + /// that referrences the blob. + /// </summary> + /// <param name="currentKey">The key that currently referrences the blob in the store</param> + /// <param name="newKey">The new key that will referrence the blob</param> + /// <param name="blob">The <see cref="BlobItem"/> if its found in the store</param> + /// <returns>True if the record was found and the key was changes</returns> + public bool TryChangeKey(string currentKey, string newKey, [NotNullWhen(true)] out MemoryHandle<byte>? blob) + { + if (LookupTable.Remove(currentKey, out LinkedListNode<KeyValuePair<string, MemoryHandle<byte>>>? node)) + { + //Remove the node from the ll + List.Remove(node); + //Update the node kvp + blob = node.Value.Value; + node.Value = new KeyValuePair<string, MemoryHandle<byte>>(newKey, blob); + //Add to end of list + List.AddLast(node); + //Re-add to lookup table with new key + LookupTable.Add(newKey, node); + return true; + } + blob = null; + return false; + } + /// <summary> + /// Removes the <see cref="BlobItem"/> from the store without disposing the blobl + /// </summary> + /// <param name="key">The key that referrences the <see cref="BlobItem"/> in the store</param> + /// <returns>A value indicating if the blob was removed</returns> + public override bool Remove(string key) + { + //Remove the item from the lookup table and if it exists, remove the node from the list + if (LookupTable.Remove(key, out LinkedListNode<KeyValuePair<string, MemoryHandle<byte>>>? node)) + { + //Remove the new from the list + List.Remove(node); + //dispose the buffer + node.Value.Value.Dispose(); + return true; + } + return false; + } + /// <summary> + /// Removes and disposes all blobl elements in cache (or in the backing store) + /// </summary> + public override void Clear() + { + foreach (MemoryHandle<byte> blob in List.Select(kp => kp.Value)) + { + blob.Dispose(); + } + base.Clear(); + } + } +} diff --git a/lib/VNLib.Data.Caching.ObjectCache/src/BlobItem.cs b/lib/VNLib.Data.Caching.ObjectCache/src/BlobItem.cs new file mode 100644 index 0000000..728875f --- /dev/null +++ b/lib/VNLib.Data.Caching.ObjectCache/src/BlobItem.cs @@ -0,0 +1,207 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Data.Caching.ObjectCache +* File: BlobItem.cs +* +* BlobItem.cs is part of VNLib.Data.Caching.ObjectCache which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Data.Caching.ObjectCache 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.ObjectCache 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.Threading; +using System.Threading.Tasks; + +using VNLib.Utils; +using VNLib.Utils.IO; +using VNLib.Utils.Memory; +using VNLib.Utils.Logging; +using VNLib.Utils.Extensions; + +namespace VNLib.Data.Caching +{ + /// <summary> + /// A general purpose binary storage item + /// </summary> + public class BlobItem //: VnDisposeable + { + /* + private static readonly JoinableTaskContext JTX = new(); + private static readonly Semaphore CentralSwapLock = new(Environment.ProcessorCount, Environment.ProcessorCount); + + private readonly VnMemoryStream _loadedData; + private bool _loaded; + + /// <summary> + /// The time the blob was last modified + /// </summary> + public DateTimeOffset LastAccessed { get; private set; } + + + /// <summary> + /// Gets the current size of the file (in bytes) as an atomic operation + /// </summary> + public int FileSize => (int)_loadedData.Length; + /// <summary> + /// The operation synchronization lock + /// </summary> + public AsyncReaderWriterLock OpLock { get; } + /// <summary> + /// Initializes a new <see cref="BlobItem"/> + /// </summary> + /// <param name="heap">The heap to allocate buffers from</param> + internal BlobItem(IUnmangedHeap heap) + { + _loadedData = new(heap); + OpLock = new AsyncReaderWriterLock(JTX); + _loaded = true; + LastAccessed = DateTimeOffset.UtcNow; + } + ///<inheritdoc/> + protected override void Free() + { + _loadedData.Dispose(); + OpLock.Dispose(); + } + + /// <summary> + /// Reads data from the internal buffer and copies it to the specified buffer. + /// Use the <see cref="FileSize"/> property to obtain the size of the internal buffer + /// </summary> + /// <param name="buffer">The buffer to copy data to</param> + /// <returns>When completed, the number of bytes copied to the buffer</returns> + public int Read(Span<byte> buffer) + { + //Make sure the blob has been swapped back into memory + if (!_loaded) + { + throw new InvalidOperationException("The blob was not loaded from the disk"); + } + //Read all data from the buffer and write it to the output buffer + _loadedData.AsSpan().CopyTo(buffer); + //Update last-accessed + LastAccessed = DateTimeOffset.UtcNow; + return (int)_loadedData.Length; + } + /// <summary> + /// Overwrites the internal buffer with the contents of the supplied buffer + /// </summary> + /// <param name="buffer">The buffer containing data to store within the blob</param> + /// <returns>A <see cref="ValueTask"/> that completes when write access has been granted and copied</returns> + /// <exception cref="InvalidOperationException"></exception> + public void Write(ReadOnlySpan<byte> buffer) + { + //Make sure the blob has been swapped back into memory + if (!_loaded) + { + throw new InvalidOperationException("The blob was not loaded from the disk"); + } + //Reset the buffer + _loadedData.SetLength(buffer.Length); + _loadedData.Seek(0, SeekOrigin.Begin); + _loadedData.Write(buffer); + LastAccessed = DateTimeOffset.UtcNow; + } + + /// <summary> + /// Writes the contents of the memory buffer to its designated file on the disk + /// </summary> + /// <param name="heap">The heap to allocate buffers from</param> + /// <param name="swapDir">The <see cref="IsolatedStorageDirectory"/> that stores the file</param> + /// <param name="filename">The name of the file to write data do</param> + /// <param name="log">A log to write errors to</param> + /// <returns>A task that completes when the swap to disk is complete</returns> + internal async Task SwapToDiskAsync(IUnmangedHeap heap, DirectoryInfo swapDir, string filename, ILogProvider log) + { + try + { + //Wait for write lock + await using (AsyncReaderWriterLock.Releaser releaser = await OpLock.WriteLockAsync()) + { + //Enter swap lock + await CentralSwapLock; + try + { + //Open swap file data stream + await using FileStream swapFile = swapDir.OpenFile(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, bufferSize: 8128); + //reset swap file + swapFile.SetLength(0); + //Seek loaded-data back to 0 before writing + _loadedData.Seek(0, SeekOrigin.Begin); + //Write loaded data to disk + await _loadedData.CopyToAsync(swapFile, 8128, heap); + } + finally + { + CentralSwapLock.Release(); + } + //Release memory held by stream + _loadedData.SetLength(0); + //Clear loaded flag + _loaded = false; + LastAccessed = DateTimeOffset.UtcNow; + } + log.Debug("Blob {name} swapped to disk", filename); + } + catch(Exception ex) + { + log.Error(ex, "Blob swap to disk error"); + } + } + /// <summary> + /// Reads the contents of the blob into a memory buffer from its designated file on disk + /// </summary> + /// <param name="heap">The heap to allocate buffers from</param> + /// <param name="swapDir">The <see cref="IsolatedStorageDirectory"/> that stores the file</param> + /// <param name="filename">The name of the file to write the blob data to</param> + /// <param name="log">A log to write errors to</param> + /// <returns>A task that completes when the swap from disk is complete</returns> + internal async Task SwapFromDiskAsync(IUnmangedHeap heap, DirectoryInfo swapDir, string filename, ILogProvider log) + { + try + { + //Wait for write lock + await using (AsyncReaderWriterLock.Releaser releaser = await OpLock.WriteLockAsync()) + { + //Enter swap lock + await CentralSwapLock; + try + { + //Open swap file data stream + await using FileStream swapFile = swapDir.OpenFile(filename, FileMode.OpenOrCreate, FileAccess.Read, bufferSize:8128); + //Copy from disk to memory + await swapFile.CopyToAsync(_loadedData, 8128, heap); + } + finally + { + CentralSwapLock.Release(); + } + //Set loaded flag + _loaded = true; + LastAccessed = DateTimeOffset.UtcNow; + } + log.Debug("Blob {name} swapped from disk", filename); + } + catch(Exception ex) + { + log.Error(ex, "Blob swap from disk error"); + } + } + */ + } +} diff --git a/lib/VNLib.Data.Caching.ObjectCache/src/CacheListener.cs b/lib/VNLib.Data.Caching.ObjectCache/src/CacheListener.cs new file mode 100644 index 0000000..7f544e1 --- /dev/null +++ b/lib/VNLib.Data.Caching.ObjectCache/src/CacheListener.cs @@ -0,0 +1,64 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Data.Caching.ObjectCache +* File: CacheListener.cs +* +* CacheListener.cs is part of VNLib.Data.Caching.ObjectCache which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Data.Caching.ObjectCache 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.ObjectCache 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 VNLib.Utils.Memory; +using VNLib.Net.Messaging.FBM.Server; + +namespace VNLib.Data.Caching +{ + /// <summary> + /// A base implementation of a memory/disk LRU data cache FBM listener + /// </summary> + public abstract class CacheListener : FBMListenerBase + { + /// <summary> + /// The directory swap files will be stored + /// </summary> + public DirectoryInfo? Directory { get; private set; } + /// <summary> + /// The Cache store to access data blobs + /// </summary> + protected BlobCache? Cache { get; private set; } + /// <summary> + /// The <see cref="IUnmangedHeap"/> to allocate buffers from + /// </summary> + protected IUnmangedHeap? Heap { get; private set; } + + /// <summary> + /// Initializes the <see cref="Cache"/> data store + /// </summary> + /// <param name="dir">The directory to swap cache records to</param> + /// <param name="cacheSize">The size of the LRU cache</param> + /// <param name="heap">The heap to allocate buffers from</param> + protected void InitCache(DirectoryInfo dir, int cacheSize, IUnmangedHeap heap) + { + Heap = heap; + Cache = new(dir, cacheSize, Log, Heap); + Directory = dir; + } + } +} diff --git a/lib/VNLib.Data.Caching.ObjectCache/src/ChangeEvent.cs b/lib/VNLib.Data.Caching.ObjectCache/src/ChangeEvent.cs new file mode 100644 index 0000000..7ee4427 --- /dev/null +++ b/lib/VNLib.Data.Caching.ObjectCache/src/ChangeEvent.cs @@ -0,0 +1,42 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Data.Caching.ObjectCache +* File: ChangeEvent.cs +* +* ChangeEvent.cs is part of VNLib.Data.Caching.ObjectCache which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Data.Caching.ObjectCache 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.ObjectCache 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.ObjectCache +{ + /// <summary> + /// An event object that is passed when change events occur + /// </summary> + public class ChangeEvent + { + public readonly string CurrentId; + public readonly string? AlternateId; + public readonly bool Deleted; + internal ChangeEvent(string id, string? alternate, bool deleted) + { + CurrentId = id; + AlternateId = alternate; + Deleted = deleted; + } + } +} diff --git a/lib/VNLib.Data.Caching.ObjectCache/src/ObjectCacheStore.cs b/lib/VNLib.Data.Caching.ObjectCache/src/ObjectCacheStore.cs new file mode 100644 index 0000000..514d00b --- /dev/null +++ b/lib/VNLib.Data.Caching.ObjectCache/src/ObjectCacheStore.cs @@ -0,0 +1,263 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Data.Caching.ObjectCache +* File: ObjectCacheStore.cs +* +* ObjectCacheStore.cs is part of VNLib.Data.Caching.ObjectCache which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Data.Caching.ObjectCache 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.ObjectCache 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.Threading; +using System.Threading.Tasks; + +using VNLib.Utils.Async; +using VNLib.Utils.Memory; +using VNLib.Utils.Logging; +using VNLib.Utils.Extensions; +using VNLib.Net.Messaging.FBM.Server; +using static VNLib.Data.Caching.Constants; + +namespace VNLib.Data.Caching.ObjectCache +{ + public delegate ReadOnlySpan<byte> GetBodyDataCallback<T>(T state); + + /// <summary> + /// A <see cref="FBMListener"/> implementation of a <see cref="CacheListener"/> + /// </summary> + public class ObjectCacheStore : CacheListener, IDisposable + { + private readonly SemaphoreSlim StoreLock; + private bool disposedValue; + + ///<inheritdoc/> + protected override ILogProvider Log { get; } + + /// <summary> + /// A queue that stores update and delete events + /// </summary> + public AsyncQueue<ChangeEvent> EventQueue { get; } + + /// <summary> + /// Initialzies a new <see cref="ObjectCacheStore"/> + /// </summary> + /// <param name="dir">The <see cref="DirectoryInfo"/> to store blob files to</param> + /// <param name="cacheMax"></param> + /// <param name="log"></param> + /// <param name="heap"></param> + /// <param name="singleReader">A value that indicates if a single thread is processing events</param> + public ObjectCacheStore(DirectoryInfo dir, int cacheMax, ILogProvider log, IUnmangedHeap heap, bool singleReader) + { + Log = log; + //We can use a single writer and single reader in this context + EventQueue = new(true, singleReader); + InitCache(dir, cacheMax, heap); + InitListener(heap); + StoreLock = new(1,1); + } + + ///<inheritdoc/> + protected override async Task ProcessAsync(FBMContext context, object? userState, CancellationToken cancellationToken) + { + try + { + //Get the action header + string action = context.Method(); + //Optional newid header + string? alternateId = context.NewObjectId(); + + switch (action) + { + case Actions.Get: + { + //Get the object-id header + string objectId = context.ObjectId(); + //Take lock on store + using SemSlimReleaser rel = await StoreLock.GetReleaserAsync(cancellationToken: cancellationToken); + if (Cache!.TryGetValue(objectId, out MemoryHandle<byte>? data)) + { + //Set the status code and write the buffered data to the response buffer + context.CloseResponse(ResponseCodes.Okay); + //Copy data to response buffer + context.Response.WriteBody(data.Span); + } + else + { + context.CloseResponse(ResponseCodes.NotFound); + } + } + break; + case Actions.AddOrUpdate: + { + //Get the object-id header + string objectId = context.ObjectId(); + //Add/update a blob async + await AddOrUpdateBlobAsync(objectId, alternateId, static context => context.Request.BodyData, context); + //Notify update the event bus + await EventQueue.EnqueueAsync(new(objectId, alternateId, false), cancellationToken); + //Set status code + context.CloseResponse(ResponseCodes.Okay); + } + break; + case Actions.Delete: + { + //Get the object-id header + string objectId = context.ObjectId(); + + if (await DeleteItemAsync(objectId)) + { + //Notify deleted + await EventQueue.EnqueueAsync(new(objectId, null, true), cancellationToken); + //Set status header + context.CloseResponse(ResponseCodes.Okay); + } + else + { + //Set status header + context.CloseResponse(ResponseCodes.NotFound); + } + } + break; + // event queue dequeue request + case Actions.Dequeue: + { + //If no event bus is registered, then this is not a legal command + if (userState is not AsyncQueue<ChangeEvent> eventBus) + { + context.CloseResponse(ResponseCodes.NotFound); + break; + } + //Wait for a new message to process + ChangeEvent ev = await eventBus.DequeueAsync(cancellationToken); + if (ev.Deleted) + { + context.CloseResponse("deleted"); + context.Response.WriteHeader(ObjectId, ev.CurrentId); + } + else + { + //Changed + context.CloseResponse("modified"); + context.Response.WriteHeader(ObjectId, ev.CurrentId); + //Set old id if an old id is set + if (ev.CurrentId != null) + { + context.Response.WriteHeader(NewObjectId, ev.AlternateId); + } + } + } + break; + } + } + catch (OperationCanceledException) + { + throw; + } + catch(Exception ex) + { + //Log error and set error status code + Log.Error(ex); + context.CloseResponse(ResponseCodes.Error); + } + } + + /// <summary> + /// Asynchronously deletes a previously stored item + /// </summary> + /// <param name="id">The id of the object to delete</param> + /// <returns>A task that completes when the item has been deleted</returns> + public async Task<bool> DeleteItemAsync(string id) + { + using SemSlimReleaser rel = await StoreLock.GetReleaserAsync(); + return Cache!.Remove(id); + } + + /// <summary> + /// Asynchronously adds or updates an object in the store and optionally update's its id + /// </summary> + /// <param name="objectId">The current (or old) id of the object</param> + /// <param name="alternateId">An optional id to update the blob to</param> + /// <param name="bodyData">A callback that returns the data for the blob</param> + /// <param name="state">The state parameter to pass to the data callback</param> + /// <returns></returns> + public async Task AddOrUpdateBlobAsync<T>(string objectId, string? alternateId, GetBodyDataCallback<T> bodyData, T state) + { + MemoryHandle<byte>? blob; + //See if new/alt session id was specified + if (string.IsNullOrWhiteSpace(alternateId)) + { + //Take lock on store + using SemSlimReleaser rel = await StoreLock.GetReleaserAsync(); + //See if blob exists + if (!Cache!.TryGetValue(objectId, out blob)) + { + //If not, create new blob and add to store + blob = Heap.AllocAndCopy(bodyData(state)); + Cache.Add(objectId, blob); + } + else + { + //Reset the buffer state + blob.WriteAndResize(bodyData(state)); + } + } + //Need to change the id of the record + else + { + //Take lock on store + using SemSlimReleaser rel = await StoreLock.GetReleaserAsync(); + //Try to change the blob key + if (!Cache!.TryChangeKey(objectId, alternateId, out blob)) + { + //Blob not found, create new blob + blob = Heap.AllocAndCopy(bodyData(state)); + Cache.Add(alternateId, blob); + } + else + { + //Reset the buffer state + blob.WriteAndResize(bodyData(state)); + } + } + } + + ///<inheritdoc/> + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + Cache?.Clear(); + } + + StoreLock.Dispose(); + + disposedValue = true; + } + } + ///<inheritdoc/> + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/lib/VNLib.Data.Caching.ObjectCache/src/VNLib.Data.Caching.ObjectCache.csproj b/lib/VNLib.Data.Caching.ObjectCache/src/VNLib.Data.Caching.ObjectCache.csproj new file mode 100644 index 0000000..119622f --- /dev/null +++ b/lib/VNLib.Data.Caching.ObjectCache/src/VNLib.Data.Caching.ObjectCache.csproj @@ -0,0 +1,34 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net6.0</TargetFramework> + + <Authors>Vaughn Nugent</Authors> + <Copyright>Copyright © 2023 Vaughn Nugent</Copyright> + <Nullable>enable</Nullable> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <PackageProjectUrl>https://www.vaughnnugent.com/resources</PackageProjectUrl> + <AssemblyVersion>1.0.0.1</AssemblyVersion> + <Version>1.0.1.1</Version> + <AnalysisLevel>latest-all</AnalysisLevel> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> + <Description>Provides server-side object-cache related infrastructure</Description> + </PropertyGroup> + + <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="..\..\VNLib.Data.Caching\src\VNLib.Data.Caching.csproj" /> + </ItemGroup> + +</Project> |