diff options
Diffstat (limited to 'lib/VNLib.Plugins.Extensions.Data/src')
3 files changed, 51 insertions, 127 deletions
diff --git a/lib/VNLib.Plugins.Extensions.Data/src/Storage/Blob.cs b/lib/VNLib.Plugins.Extensions.Data/src/Storage/Blob.cs index 3fca1cb..0edf653 100644 --- a/lib/VNLib.Plugins.Extensions.Data/src/Storage/Blob.cs +++ b/lib/VNLib.Plugins.Extensions.Data/src/Storage/Blob.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Extensions.Data @@ -24,6 +24,7 @@ using System; using System.IO; +using System.Threading; using System.Threading.Tasks; using System.Runtime.Versioning; @@ -206,7 +207,7 @@ namespace VNLib.Plugins.Extensions.Data.Storage GC.SuppressFinalize(this); } ///<inheritdoc/> - public async ValueTask ReleaseAsync() + public async ValueTask ReleaseAsync(CancellationToken cancellation = default) { try { @@ -237,7 +238,7 @@ namespace VNLib.Plugins.Extensions.Data.Storage //Dispose the stream await BaseStream.DisposeAsync(); //Release the descriptor - await Descriptor.ReleaseAsync(); + await Descriptor.ReleaseAsync(cancellation); } } } diff --git a/lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageDescriptor.cs b/lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageDescriptor.cs index d410187..78fdfa0 100644 --- a/lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageDescriptor.cs +++ b/lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageDescriptor.cs @@ -23,17 +23,15 @@ */ using System; -using System.IO; using System.Text.Json; +using System.Threading; using System.Collections; -using System.IO.Compression; using System.Threading.Tasks; using System.Collections.Generic; using System.Text.Json.Serialization; using VNLib.Utils; using VNLib.Utils.Async; -using VNLib.Utils.Memory; namespace VNLib.Plugins.Extensions.Data.Storage { @@ -60,35 +58,32 @@ namespace VNLib.Plugins.Extensions.Data.Storage private readonly Lazy<Dictionary<string, string>> StringStorage; + protected override IAsyncResourceStateHandler AsyncHandler { get; } + /// <summary> /// The currnt descriptor's identifier string within its backing table. Usually the primary key. /// </summary> public string DescriptorID => Entry.Id; + /// <summary> /// The identifier of the user for which this descriptor belongs to /// </summary> public string UserID => Entry.UserId!; + /// <summary> /// The <see cref="DateTime"/> when the descriptor was created /// </summary> public DateTimeOffset Created => Entry.Created; + /// <summary> /// The last time this descriptor was updated /// </summary> public DateTimeOffset LastModified => Entry.LastModified; - ///<inheritdoc/> - protected override AsyncUpdateCallback UpdateCb { get; } - ///<inheritdoc/> - protected override AsyncDeleteCallback DeleteCb { get; } - ///<inheritdoc/> - protected override JsonSerializerOptions JSO => SerializerOptions; - - internal LWStorageDescriptor(LWStorageManager manager, LWStorageEntry entry) + internal LWStorageDescriptor(IAsyncResourceStateHandler handler, LWStorageEntry entry) { Entry = entry; - UpdateCb = manager.UpdateDescriptorAsync; - DeleteCb = manager.RemoveDescriptorAsync; + AsyncHandler = handler; StringStorage = new(OnStringStoreLoad); } @@ -100,15 +95,8 @@ namespace VNLib.Plugins.Extensions.Data.Storage } else { - //Calc and alloc decode buffer - int bufferSize = (int)(Entry.Data.Length * 1.75); - - using UnsafeMemoryHandle<byte> decodeBuffer = MemoryUtil.UnsafeAlloc(bufferSize); - //Decode and deserialize the data - return BrotliDecoder.TryDecompress(Entry.Data, decodeBuffer, out int written) - ? JsonSerializer.Deserialize<Dictionary<string, string>>(decodeBuffer.Span[..written], SerializerOptions) ?? new(StringComparer.OrdinalIgnoreCase) - : throw new InvalidDataException("Failed to decompress data"); + return JsonSerializer.Deserialize<Dictionary<string, string>>(Entry.Data, SerializerOptions) ?? new(StringComparer.OrdinalIgnoreCase); } } @@ -142,7 +130,6 @@ namespace VNLib.Plugins.Extensions.Data.Storage SetStringValue(key, value); } } - /// <summary> /// Gets a string value from string storage matching a given key @@ -211,9 +198,10 @@ namespace VNLib.Plugins.Extensions.Data.Storage } ///<inheritdoc/> - public override async ValueTask ReleaseAsync() + public override async ValueTask ReleaseAsync(CancellationToken cancellation = default) { - await base.ReleaseAsync(); + await base.ReleaseAsync(cancellation); + //Cleanup dict on exit if (StringStorage.IsValueCreated) { @@ -223,8 +211,15 @@ namespace VNLib.Plugins.Extensions.Data.Storage ///<inheritdoc/> public IEnumerator<KeyValuePair<string, string>> GetEnumerator() => StringStorage.Value.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + ///<inheritdoc/> - protected override object GetResource() => StringStorage.Value; + protected override object GetResource() + { + //Serlaize the state data and store it in the data entry + Entry.Data = JsonSerializer.SerializeToUtf8Bytes(StringStorage.Value, SerializerOptions); + return Entry; + } } }
\ No newline at end of file diff --git a/lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageManager.cs b/lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageManager.cs index eabe618..bc506c7 100644 --- a/lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageManager.cs +++ b/lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageManager.cs @@ -23,18 +23,17 @@ */ using System; -using System.IO; using System.Data; using System.Linq; using System.Threading; -using System.IO.Compression; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using VNLib.Utils; -using VNLib.Utils.IO; -using VNLib.Utils.Memory; +using VNLib.Utils.Async; +using VNLib.Plugins.Extensions.Data.Extensions; + namespace VNLib.Plugins.Extensions.Data.Storage { @@ -42,8 +41,9 @@ namespace VNLib.Plugins.Extensions.Data.Storage /// <summary> /// Provides single table database object storage services /// </summary> - public sealed class LWStorageManager + public sealed class LWStorageManager : IAsyncResourceStateHandler { + /// <summary> /// The generator function that is invoked when a new <see cref="LWStorageDescriptor"/> is to /// be created without an explicit id @@ -112,20 +112,11 @@ namespace VNLib.Plugins.Extensions.Data.Storage //Add and save changes ctx.Descriptors.Add(entry); - ERRNO result = await ctx.SaveChangesAsync(cancellation); + ERRNO result = await ctx.SaveAndCloseAsync(true, cancellation); - if (!result) - { - //Rollback and raise exception - await ctx.RollbackTransctionAsync(cancellation); - throw new LWDescriptorCreationException("Failed to create descriptor, because changes could not be saved"); - } - else - { - //Commit transaction and return the new descriptor - await ctx.CommitTransactionAsync(cancellation); - return new LWStorageDescriptor(this, entry); - } + return result + ? new LWStorageDescriptor(this, entry) + : throw new LWDescriptorCreationException("Failed to create descriptor, because changes could not be saved"); } /// <summary> @@ -154,17 +145,10 @@ namespace VNLib.Plugins.Extensions.Data.Storage select s) .SingleOrDefaultAsync(cancellation); + await db.SaveAndCloseAsync(true, cancellation); + //Close transactions and return - if (entry == null) - { - await db.RollbackTransctionAsync(cancellation); - return null; - } - else - { - await db.CommitTransactionAsync(cancellation); - return new (this, entry); - } + return entry == null ? null : new (this, entry); } /// <summary> @@ -193,17 +177,10 @@ namespace VNLib.Plugins.Extensions.Data.Storage select s) .SingleOrDefaultAsync(cancellation); + await db.SaveAndCloseAsync(true, cancellation); + //Close transactions and return - if (entry == null) - { - await db.RollbackTransctionAsync(cancellation); - return null; - } - else - { - await db.CommitTransactionAsync(cancellation); - return new (this, entry); - } + return entry == null ? null : new(this, entry); } /// <summary> @@ -236,61 +213,27 @@ namespace VNLib.Plugins.Extensions.Data.Storage //Delete db.Descriptors.RemoveRange(expired); - //Save changes - ERRNO count = await db.SaveChangesAsync(cancellation); - //Commit transaction - await db.CommitTransactionAsync(cancellation); - - return count; + return await db.SaveAndCloseAsync(true, cancellation); } - - /// <summary> - /// Updates a descriptor's data field - /// </summary> - /// <param name="descriptorObj">Descriptor to update</param> - /// <param name="data">Data string to store to descriptor record</param> - /// <exception cref="LWStorageUpdateFailedException"></exception> - internal async Task UpdateDescriptorAsync(object descriptorObj, Stream data) + + async Task IAsyncResourceStateHandler.UpdateAsync(AsyncUpdatableResource resource, object state, CancellationToken cancellation) { - LWStorageEntry entry = (descriptorObj as LWStorageDescriptor)!.Entry; + LWStorageEntry entry = (state as LWStorageEntry)!; ERRNO result = 0; try { await using LWStorageContext ctx = GetContext(); - await ctx.OpenTransactionAsync(CancellationToken.None); + await ctx.OpenTransactionAsync(System.Transactions.IsolationLevel.RepeatableRead, cancellation); //Begin tracking ctx.Descriptors.Attach(entry); - - //Convert stream to vnstream - VnMemoryStream vms = (VnMemoryStream)data; - using (IMemoryHandle<byte> encBuffer = MemoryUtil.SafeAlloc<byte>((int)vms.Length)) - { - //try to compress - if(!BrotliEncoder.TryCompress(vms.AsSpan(), encBuffer.Span, out int compressed)) - { - throw new InvalidDataException("Failed to compress the descriptor data"); - } - - //Set the data - entry.Data = encBuffer.Span[..compressed].ToArray(); - } + //Update modified time entry.LastModified = DateTime.UtcNow; //Save changes - result = await ctx.SaveChangesAsync(CancellationToken.None); - - //Commit or rollback - if (result) - { - await ctx.CommitTransactionAsync(CancellationToken.None); - } - else - { - await ctx.RollbackTransctionAsync(CancellationToken.None); - } + result = await ctx.SaveAndCloseAsync(true, cancellation); } catch (Exception ex) { @@ -302,37 +245,23 @@ namespace VNLib.Plugins.Extensions.Data.Storage throw new LWStorageUpdateFailedException($"Descriptor {entry.Id} failed to update"); } } - - /// <summary> - /// Function to remove the specified descriptor - /// </summary> - /// <param name="descriptorObj">The active descriptor to remove from the database</param> - /// <exception cref="LWStorageRemoveFailedException"></exception> - internal async Task RemoveDescriptorAsync(object descriptorObj) + + async Task IAsyncResourceStateHandler.DeleteAsync(AsyncUpdatableResource resource, CancellationToken cancellation) { - LWStorageEntry descriptor = (descriptorObj as LWStorageDescriptor)!.Entry; + LWStorageEntry descriptor = (resource as LWStorageDescriptor)!.Entry; ERRNO result; try { //Init db await using LWStorageContext db = GetContext(); //Begin transaction - await db.OpenTransactionAsync(); + await db.OpenTransactionAsync(cancellation); //Delete the user from the database db.Descriptors.Remove(descriptor); //Save changes and commit if successful - result = await db.SaveChangesAsync(); - - if (result) - { - await db.CommitTransactionAsync(); - } - else - { - await db.RollbackTransctionAsync(); - } + result = await db.SaveAndCloseAsync(true, cancellation); } catch (Exception ex) { @@ -343,6 +272,5 @@ namespace VNLib.Plugins.Extensions.Data.Storage throw new LWStorageRemoveFailedException("Failed to delete the user account because of a database failure, the user may already be deleted"); } } - } }
\ No newline at end of file |