From 1e81a5b2b7e35551690dfdb7f6866959d25993a3 Mon Sep 17 00:00:00 2001 From: Lilith River Date: Fri, 26 Jan 2024 11:52:35 -0700 Subject: [PATCH] WIP --- src/Imazen.Routing/Layers/LocalFilesLayer.cs | 8 +++++++ .../Promises/IInstantPromise.cs | 22 +++++++++++++++++-- .../Promises/Pipelines/ImagingMiddleware.cs | 17 ++++++++------ .../Pipelines/ServerlessCacheEngine.cs | 14 ++++++++---- src/Imazen.Routing/Serving/ImageServer.cs | 3 ++- 5 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/Imazen.Routing/Layers/LocalFilesLayer.cs b/src/Imazen.Routing/Layers/LocalFilesLayer.cs index eb4d1e3..bdc6482 100644 --- a/src/Imazen.Routing/Layers/LocalFilesLayer.cs +++ b/src/Imazen.Routing/Layers/LocalFilesLayer.cs @@ -123,6 +123,8 @@ public async ValueTask CreateResponseAsync(IRequestSnaps { return new BlobResponse(await TryGetBlobAsync(request, router, pipeline, cancellationToken)).AsResponse(); } + + public bool HasDependencies => false; public bool ReadyToWriteCacheKeyBasisData => true; @@ -136,6 +138,12 @@ public virtual void WriteCacheKeyBasisPairsToRecursive(IBufferWriter write FinalRequest.WriteCacheKeyBasisPairsTo(writer); } + private byte[]? cacheKey32Bytes = null; + public byte[] GetCacheKey32Bytes() + { + return cacheKey32Bytes ??= this.GetCacheKey32BytesUncached(); + } + public virtual bool SupportsPreSignedUrls => false; public abstract ValueTask> TryGetBlobAsync(IRequestSnapshot request, diff --git a/src/Imazen.Routing/Promises/IInstantPromise.cs b/src/Imazen.Routing/Promises/IInstantPromise.cs index 380a8df..8cf3dcc 100644 --- a/src/Imazen.Routing/Promises/IInstantPromise.cs +++ b/src/Imazen.Routing/Promises/IInstantPromise.cs @@ -40,6 +40,8 @@ public interface IInstantCacheKeySupportingPromise: IInstantPromise // Must call resolve dependencies first void WriteCacheKeyBasisPairsToRecursive(IBufferWriter writer); + + byte[] GetCacheKey32Bytes(); } public interface ICacheableBlobPromise : IInstantCacheKeySupportingPromise { @@ -55,7 +57,16 @@ public interface ISupportsPreSignedUrls : ICacheableBlobPromise public static class CacheableBlobPromiseExtensions { - + public static byte[] GetCacheKey32BytesUncached(this ICacheableBlobPromise promise) + { + var buffer = new byte[32]; + var used = promise.CopyCacheKeyBytesTo(buffer); + if (used.Length != 32) + { + throw new InvalidOperationException("Hash buffer failure 1125125"); + } + return buffer; + } public static ReadOnlySpan CopyCacheKeyBytesTo(this ICacheableBlobPromise promise, Span buffer32Bytes) { if (buffer32Bytes.Length < 32) @@ -66,11 +77,12 @@ public static ReadOnlySpan CopyCacheKeyBytesTo(this ICacheableBlobPromise { throw new InvalidOperationException("Promise is not ready to write cache key basis data. Did you call RouteDependenciesAsync? Check ReadyToWriteCacheKeyBasisData first"); } - var buffer = new ArrayPoolBufferWriter(4096); + using var buffer = new ArrayPoolBufferWriter(4096); promise.WriteCacheKeyBasisPairsToRecursive(buffer); #if NET5_0_OR_GREATER var bytesWritten = SHA256.HashData(buffer.WrittenSpan, buffer32Bytes); +buffer.Dispose(); return buffer32Bytes[..bytesWritten]; #elif NETSTANDARD2_0 using var hasher = SHA256.Create(); @@ -96,6 +108,12 @@ public record CacheableBlobPromise(IRequestSnapshot FinalRequest, Func true; public bool SupportsPreSignedUrls => false; + private byte[]? cacheKey32Bytes = null; + public byte[] GetCacheKey32Bytes() + { + return cacheKey32Bytes ??= this.GetCacheKey32BytesUncached(); + } + public ValueTask RouteDependenciesAsync(IBlobRequestRouter router, CancellationToken cancellationToken = default) { return Tasks.ValueResult(CodeResult.Ok()); diff --git a/src/Imazen.Routing/Promises/Pipelines/ImagingMiddleware.cs b/src/Imazen.Routing/Promises/Pipelines/ImagingMiddleware.cs index 5024bb2..4df5fb9 100644 --- a/src/Imazen.Routing/Promises/Pipelines/ImagingMiddleware.cs +++ b/src/Imazen.Routing/Promises/Pipelines/ImagingMiddleware.cs @@ -113,7 +113,7 @@ public ImagingPromise(ImagingMiddlewareOptions options, PromisePipeline = promisePipeline; Options = options; AppliedWatermarks = appliedWatermarks; - FinalCommandString = finalCommandString; + FinalCommandString = finalCommandString.TrimStart('?'); // determine if we have extra dependencies @@ -134,7 +134,12 @@ public ImagingPromise(ImagingMiddlewareOptions options, Dependencies = new List(){Input0}; } } - + private byte[]? cacheKey32Bytes = null; + public byte[] GetCacheKey32Bytes() + { + return cacheKey32Bytes ??= this.GetCacheKey32BytesUncached(); + } + private string FinalCommandString { get; init; } public async ValueTask> TryGetBlobAsync(IRequestSnapshot request, @@ -189,12 +194,10 @@ public async ValueTask> TryGetBlobAsync(IRequestSnapsho { watermarks = new List(AppliedWatermarks.Count); watermarks.AddRange( - AppliedWatermarks.Select((t, i) => - { - return new InputWatermark( + AppliedWatermarks.Select((t, i) + => new InputWatermark( byteSources[i + 1], - t.Watermark); - })); + t.Watermark))); } try diff --git a/src/Imazen.Routing/Promises/Pipelines/ServerlessCacheEngine.cs b/src/Imazen.Routing/Promises/Pipelines/ServerlessCacheEngine.cs index 351ee39..8743adb 100644 --- a/src/Imazen.Routing/Promises/Pipelines/ServerlessCacheEngine.cs +++ b/src/Imazen.Routing/Promises/Pipelines/ServerlessCacheEngine.cs @@ -71,7 +71,7 @@ public ServerlessCacheEngine(IBlobPromisePipeline? next, ServerlessCacheEngineOp } public async ValueTask> GetFinalPromiseAsync(ICacheableBlobPromise promise, IBlobRequestRouter router, - IBlobPromisePipeline promisePipeline,IHttpRequestStreamAdapter outerRequest, CancellationToken cancellationToken = default) + IBlobPromisePipeline promisePipeline, IHttpRequestStreamAdapter outerRequest, CancellationToken cancellationToken = default) { var wrappedPromise = promise; if (Next != null) @@ -97,9 +97,8 @@ public async ValueTask> GetFinalPromiseAsync(I public static IBlobCacheRequest For(ICacheableBlobPromise promise, BlobGroup blobGroup) { - var bytes = new byte[32]; - var hex = promise.CopyCacheKeyBytesTo(bytes) - .ToHexLowercase(); + var bytes = promise.GetCacheKey32Bytes(); + var hex = bytes.ToHexLowercase(); return new BlobCacheRequest(blobGroup, bytes, hex, false); } @@ -415,6 +414,13 @@ internal record ServerlessCachePromise(IRequestSnapshot FinalRequest, ICacheable public bool HasDependencies => FreshPromise.HasDependencies; public bool ReadyToWriteCacheKeyBasisData => FreshPromise.ReadyToWriteCacheKeyBasisData; public bool SupportsPreSignedUrls => FreshPromise.SupportsPreSignedUrls; + + private byte[]? cacheKey32Bytes = null; + public byte[] GetCacheKey32Bytes() + { + return cacheKey32Bytes ??= this.GetCacheKey32BytesUncached(); + } + public ValueTask RouteDependenciesAsync(IBlobRequestRouter router, CancellationToken cancellationToken = default) { return FreshPromise.RouteDependenciesAsync(router, cancellationToken); diff --git a/src/Imazen.Routing/Serving/ImageServer.cs b/src/Imazen.Routing/Serving/ImageServer.cs index 1271202..d9cf3df 100644 --- a/src/Imazen.Routing/Serving/ImageServer.cs +++ b/src/Imazen.Routing/Serving/ImageServer.cs @@ -89,7 +89,7 @@ public ImageServer(IImageServerContainer container, }; pipeline = new ServerlessCacheEngine(null, sourceCacheOptions); - pipeline = new ImagingMiddleware(pipeline, imagingOptions); + pipeline = new ImagingMiddleware(null, imagingOptions); pipeline = new ServerlessCacheEngine(pipeline, sourceCacheOptions); } @@ -196,6 +196,7 @@ public async ValueTask TryHandleRequestAsync(TRequest request, TResponse r var blob = blobResult.Unwrap(); // TODO: if the blob provided an etag, it could be from blob storage, or it could be from a cache. + // TODO: TryGetBlobAsync already calculates the cache if it's a serverless promise... // Since cache provider has to calculate the cache key anyway, can't we figure out how to improve this? promisedEtag ??= CreateEtag(finalPromise);