-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Add HybridCache public API #55084
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Add HybridCache public API #55084
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
35e9cea
initial API cut (post review)
mgravell 12b3a0e
basic API test
mgravell c5d28d4
prove that the API can be configured
mgravell 20e1cb2
demonstrate serializer/factory configuration working
mgravell 4379647
move to NuGet only to make the build happier
mgravell d382a7a
defer on trimming
mgravell e8bc9a9
PR review comments
mgravell 7edddb1
tyop
mgravell dc622a8
Update src/Caching/Hybrid/src/Runtime/IsExternalInit.cs
mgravell 4920182
return a leased array on netfx
mgravell 8841e97
prefer ForEach to Walk
mgravell 6cbe02e
Update src/Caching/Hybrid/src/Internal/InbuiltTypeSerializer.cs
mgravell 0c0d589
Update src/Caching/Hybrid/src/Internal/DefaultJsonSerializerFactory.cs
mgravell ef0ad56
Update src/Caching/Hybrid/src/Internal/InbuiltTypeSerializer.cs
mgravell e28f316
comment nits
mgravell fbedef4
Merge branch 'marc/hybrid-api' of https://github.com/dotnet/aspnetcor…
mgravell b49151e
use TimeProvider throughout
mgravell c2326fd
more nits
mgravell 29dcc2e
regen projects list
mgravell 4ddba7f
TryAdd
mgravell 427601c
clarify intent of null on Remove{Tags|Keys}Async
mgravell 1db2758
Update src/Caching/Hybrid/src/Runtime/HybridCache.cs
mgravell 2945ce0
Update src/Caching/Hybrid/src/Runtime/HybridCache.cs
mgravell 94d9fe1
Update src/Caching/Hybrid/src/Runtime/HybridCache.cs
mgravell a13e1d3
Update src/Caching/Hybrid/src/Runtime/HybridCache.cs
mgravell 2a77920
Update src/Caching/Hybrid/src/Runtime/HybridCache.cs
mgravell cee8ddc
Update src/Caching/Hybrid/src/Runtime/HybridCache.cs
mgravell 7989f16
Update src/Caching/Hybrid/src/Runtime/HybridCacheEntryFlags.cs
mgravell 49a477f
Update src/Caching/Hybrid/src/Runtime/HybridCache.cs
mgravell 5f8bcb8
Update src/Caching/Hybrid/src/Runtime/HybridCacheEntryOptions.cs
mgravell 7b32e0f
Update src/Caching/Hybrid/src/Runtime/HybridCache.cs
mgravell 62b4e6f
Update src/Caching/Hybrid/src/Runtime/IBufferDistributedCache.cs
mgravell c6cdeae
Apply suggestions from code review
mgravell d624726
more PR feedback
mgravell File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace Microsoft.Extensions.Caching.Hybrid; | ||
|
||
/// <summary> | ||
/// Configuration extension methods for <see cref="IHybridCacheBuilder"/> / <see cref="HybridCache"/>. | ||
/// </summary> | ||
public static class HybridCacheBuilderExtensions | ||
{ | ||
/// <summary> | ||
/// Serialize values of type <typeparamref name="T"/> with the specified serializer from <paramref name="serializer"/>. | ||
/// </summary> | ||
public static IHybridCacheBuilder WithSerializer<T>(this IHybridCacheBuilder builder, IHybridCacheSerializer<T> serializer) | ||
{ | ||
builder.Services.AddSingleton<IHybridCacheSerializer<T>>(serializer); | ||
return builder; | ||
} | ||
|
||
/// <summary> | ||
/// Serialize values of type <typeparamref name="T"/> with the serializer of type <typeparamref name="TImplementation"/>. | ||
/// </summary> | ||
public static IHybridCacheBuilder WithSerializer<T, | ||
#if NET5_0_OR_GREATER | ||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] | ||
#endif | ||
TImplementation>(this IHybridCacheBuilder builder) | ||
where TImplementation : class, IHybridCacheSerializer<T> | ||
{ | ||
builder.Services.AddSingleton<IHybridCacheSerializer<T>, TImplementation>(); | ||
return builder; | ||
} | ||
|
||
/// <summary> | ||
/// Add <paramref name="factory"/> as an additional serializer factory, which can provide serializers for multiple types. | ||
/// </summary> | ||
public static IHybridCacheBuilder WithSerializerFactory(this IHybridCacheBuilder builder, IHybridCacheSerializerFactory factory) | ||
{ | ||
builder.Services.AddSingleton<IHybridCacheSerializerFactory>(factory); | ||
return builder; | ||
} | ||
|
||
/// <summary> | ||
/// Add a factory of type <typeparamref name="TImplementation"/> as an additional serializer factory, which can provide serializers for multiple types. | ||
/// </summary> | ||
public static IHybridCacheBuilder WithSerializerFactory< | ||
#if NET5_0_OR_GREATER | ||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] | ||
#endif | ||
TImplementation>(this IHybridCacheBuilder builder) | ||
where TImplementation : class, IHybridCacheSerializerFactory | ||
{ | ||
builder.Services.AddSingleton<IHybridCacheSerializerFactory, TImplementation>(); | ||
return builder; | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace Microsoft.Extensions.Caching.Hybrid; | ||
|
||
/// <summary> | ||
/// Options for configuring the default <see cref="HybridCache"/> implementation. | ||
/// </summary> | ||
public class HybridCacheOptions | ||
{ | ||
/// <summary> | ||
/// Default global options to be applied to <see cref="HybridCache"/> operations; if options are | ||
/// specified at the individual call level, the non-null values are merged (with the per-call | ||
/// options being used in preference to the global options). If no value is specified for a given | ||
/// option (globally or per-call), the implementation may choose a reasonable default. | ||
/// </summary> | ||
public HybridCacheEntryOptions? DefaultEntryOptions { get; set; } | ||
|
||
/// <summary> | ||
/// Disallow compression for this <see cref="HybridCache"/> instance. | ||
/// </summary> | ||
public bool DisableCompression { get; set; } | ||
|
||
/// <summary> | ||
/// The maximum size of cache items; attempts to store values over this size will be logged | ||
/// and the value will not be stored in cache. | ||
/// </summary> | ||
/// <remarks>The default value is 1 MiB.</remarks> | ||
public long MaximumPayloadBytes { get; set; } = 1 << 20; // 1MiB | ||
|
||
/// <summary> | ||
/// The maximum permitted length (in characters) of keys; attempts to use keys over this size will be logged. | ||
/// </summary> | ||
mgravell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// <remark>The default value is 1024 characters.</remark> | ||
public int MaximumKeyLength { get; set; } = 1024; // characters | ||
|
||
/// <summary> | ||
/// Use "tags" data as dimensions on metric reporting; if enabled, care should be used to ensure that | ||
/// tags do not contain data that should not be visible in metrics systems. | ||
/// </summary> | ||
public bool ReportTagMetrics { get; set; } | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Caching.Hybrid.Internal; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.DependencyInjection.Extensions; | ||
using Microsoft.Extensions.Internal; | ||
|
||
namespace Microsoft.Extensions.Caching.Hybrid; | ||
|
||
/// <summary> | ||
/// Configuration extension methods for <see cref="HybridCache"/>. | ||
/// </summary> | ||
public static class HybridCacheServiceExtensions | ||
{ | ||
/// <summary> | ||
/// Adds support for multi-tier caching services. | ||
/// </summary> | ||
/// <returns>A builder instance that allows further configuration of the <see cref="HybridCache"/> system.</returns> | ||
public static IHybridCacheBuilder AddHybridCache(this IServiceCollection services, Action<HybridCacheOptions> setupAction) | ||
{ | ||
#if NET7_0_OR_GREATER | ||
ArgumentNullException.ThrowIfNull(setupAction); | ||
#else | ||
_ = setupAction ?? throw new ArgumentNullException(nameof(setupAction)); | ||
#endif | ||
AddHybridCache(services); | ||
services.Configure(setupAction); | ||
return new HybridCacheBuilder(services); | ||
} | ||
|
||
/// <summary> | ||
/// Adds support for multi-tier caching services. | ||
/// </summary> | ||
/// <returns>A builder instance that allows further configuration of the <see cref="HybridCache"/> system.</returns> | ||
public static IHybridCacheBuilder AddHybridCache(this IServiceCollection services) | ||
{ | ||
#if NET7_0_OR_GREATER | ||
ArgumentNullException.ThrowIfNull(services); | ||
#else | ||
_ = services ?? throw new ArgumentNullException(nameof(services)); | ||
#endif | ||
|
||
services.TryAddSingleton(TimeProvider.System); | ||
services.AddOptions(); | ||
services.AddMemoryCache(); | ||
services.AddDistributedMemoryCache(); // we need a backend; use in-proc by default | ||
services.TryAddSingleton<IHybridCacheSerializerFactory, DefaultJsonSerializerFactory>(); | ||
services.TryAddSingleton<IHybridCacheSerializer<string>>(InbuiltTypeSerializer.Instance); | ||
services.TryAddSingleton<IHybridCacheSerializer<byte[]>>(InbuiltTypeSerializer.Instance); | ||
services.TryAddSingleton<HybridCache, DefaultHybridCache>(); | ||
return new HybridCacheBuilder(services); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace Microsoft.Extensions.Caching.Hybrid; | ||
|
||
/// <summary> | ||
/// Helper API for configuring <see cref="HybridCache"/>. | ||
/// </summary> | ||
public interface IHybridCacheBuilder | ||
{ | ||
/// <summary> | ||
/// Gets the services collection associated with this instance. | ||
/// </summary> | ||
IServiceCollection Services { get; } | ||
} | ||
|
||
internal sealed class HybridCacheBuilder(IServiceCollection services) : IHybridCacheBuilder | ||
{ | ||
public IServiceCollection Services { get; } = services; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Caching.Distributed; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Options; | ||
|
||
namespace Microsoft.Extensions.Caching.Hybrid.Internal; | ||
mgravell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// <summary> | ||
/// The inbuilt ASP.NET implementation of <see cref="HybridCache"/>. | ||
/// </summary> | ||
internal sealed class DefaultHybridCache : HybridCache | ||
{ | ||
private readonly IDistributedCache _backendCache; | ||
private readonly IServiceProvider _services; | ||
private readonly HybridCacheOptions _options; | ||
|
||
public DefaultHybridCache(IOptions<HybridCacheOptions> options, IDistributedCache backendCache, IServiceProvider services) | ||
mgravell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
_backendCache = backendCache ?? throw new ArgumentNullException(nameof(backendCache)); | ||
_services = services ?? throw new ArgumentNullException(nameof(services)); | ||
_options = options.Value; | ||
} | ||
|
||
internal HybridCacheOptions Options => _options; | ||
|
||
public override ValueTask<T> GetOrCreateAsync<TState, T>(string key, TState state, Func<TState, CancellationToken, ValueTask<T>> underlyingDataCallback, HybridCacheEntryOptions? options = null, IReadOnlyCollection<string>? tags = null, CancellationToken token = default) | ||
=> underlyingDataCallback(state, token); // pass-thru without caching for initial API pass | ||
|
||
public override ValueTask RemoveKeyAsync(string key, CancellationToken token = default) | ||
=> default; // no cache, nothing to remove | ||
|
||
public override ValueTask RemoveTagAsync(string tag, CancellationToken token = default) | ||
=> default; // no cache, nothing to remove | ||
|
||
public override ValueTask SetAsync<T>(string key, T value, HybridCacheEntryOptions? options = null, IReadOnlyCollection<string>? tags = null, CancellationToken token = default) | ||
=> default; // no cache, nothing to set | ||
|
||
internal IHybridCacheSerializer<T> GetSerializer<T>() | ||
{ | ||
// unused API, primarily intended to show configuration is working; | ||
// the real version would memoize the result | ||
var service = _services.GetService<IHybridCacheSerializer<T>>(); | ||
if (service is null) | ||
{ | ||
foreach (var factory in _services.GetServices<IHybridCacheSerializerFactory>()) | ||
{ | ||
if (factory.TryCreateSerializer<T>(out var current)) | ||
{ | ||
service = current; | ||
} | ||
} | ||
} | ||
return service ?? throw new InvalidOperationException("No serializer configured for type: " + typeof(T).Name); | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
src/Caching/Hybrid/src/Internal/DefaultJsonSerializerFactory.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Buffers; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Text.Json; | ||
|
||
namespace Microsoft.Extensions.Caching.Hybrid.Internal; | ||
|
||
internal sealed class DefaultJsonSerializerFactory : IHybridCacheSerializerFactory | ||
mgravell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
public bool TryCreateSerializer<T>([NotNullWhen(true)] out IHybridCacheSerializer<T>? serializer) | ||
{ | ||
// no restriction | ||
serializer = new DefaultJsonSerializer<T>(); | ||
BrennanConroy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return true; | ||
} | ||
|
||
internal sealed class DefaultJsonSerializer<T> : IHybridCacheSerializer<T> | ||
{ | ||
T IHybridCacheSerializer<T>.Deserialize(ReadOnlySequence<byte> source) | ||
{ | ||
var reader = new Utf8JsonReader(source); | ||
#pragma warning disable IL2026, IL3050 // AOT bits | ||
return JsonSerializer.Deserialize<T>(ref reader)!; | ||
#pragma warning restore IL2026, IL3050 | ||
} | ||
|
||
void IHybridCacheSerializer<T>.Serialize(T value, IBufferWriter<byte> target) | ||
{ | ||
using var writer = new Utf8JsonWriter(target); | ||
#pragma warning disable IL2026, IL3050 // AOT bits | ||
JsonSerializer.Serialize<T>(writer, value, JsonSerializerOptions.Default); | ||
#pragma warning restore IL2026, IL3050 | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.