Skip to content
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

YarpOutputCachePolicyProvider breaks AOT compilation in 2.2.0 #2598

Open
DavidZidar opened this issue Sep 8, 2024 · 2 comments
Open

YarpOutputCachePolicyProvider breaks AOT compilation in 2.2.0 #2598

DavidZidar opened this issue Sep 8, 2024 · 2 comments
Labels
help wanted We will welcome a contribution Type: Bug Something isn't working
Milestone

Comments

@DavidZidar
Copy link

DavidZidar commented Sep 8, 2024

Describe the bug

I just updated to 2.2.0 and while validating my proxy I get an exception during startup caused by YarpOutputCachePolicyProvider, probably because it is using reflection and it's trying access code that is trimmed away. I'm not even using output caching.

Here's the relevant part of the stack trace:

Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Reflection.DynamicInvokeInfo..ctor(MethodBase, IntPtr) + 0x314
   at Internal.Reflection.Execution.ExecutionEnvironmentImplementation.TryGetMethodInvokeInfo(RuntimeTypeHandle, QMethodDefinition, RuntimeTypeHandle[], MethodBase, MethodSignatureComparer&, CanonicalFormKind) + 0x191
   at Internal.Reflection.Execution.ExecutionEnvironmentImplementation.TryGetMethodInvoker(RuntimeTypeHandle, QMethodDefinition, RuntimeTypeHandle[]) + 0xe3
   at Internal.Reflection.Core.Execution.ExecutionEnvironment.GetMethodInvoker(RuntimeTypeInfo, QMethodDefinition, RuntimeTypeInfo[], MemberInfo, Exception&) + 0x11e
   at System.Reflection.Runtime.MethodInfos.NativeFormat.NativeFormatMethodCommon.GetUncachedMethodInvoker(RuntimeTypeInfo[], MemberInfo, Exception&) + 0x50
   at System.Reflection.Runtime.MethodInfos.RuntimeNamedMethodInfo`1.GetUncachedMethodInvoker(RuntimeTypeInfo[], MemberInfo) + 0x1b
   at System.Reflection.Runtime.PropertyInfos.RuntimePropertyInfo.GetValue(Object, BindingFlags, Binder, Object[], CultureInfo) + 0x54
   at System.Reflection.PropertyInfo.GetValue(Object, Object[]) + 0x1d
   at Yarp.ReverseProxy.Configuration.YarpOutputCachePolicyProvider..ctor(IOptions`1 outputCacheOptions) + 0xa7

Here is what I think the offending code is:

var type = typeof(OutputCacheOptions);
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
var proprety = type.GetProperty("NamedPolicies", flags);

To Reproduce

Publish using PublishAot and attempt to run the application.

Potential fix

You might be able to use the UnsafeAccessorAttribute in .NET 8:

// Define an unsafe accessor
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_NamedPolicies")]
private static extern Dictionary<string, IOutputCachePolicy>? GetNamedPolicies(OutputCacheOptions c);

// Then use it like this
_policyMap = GetNamedPolicies(_outputCacheOptions);

For older versions of .NET you need to make sure that the code you are trying to use isn't being trimmed, one way I've successfully done this in my own projects is to create an unused field that references the type with the DynamicallyAccessedMembersAttribute stating what I need to keep.

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
private static readonly Type keepOutputCacheOptions = typeof(OutputCacheOptions);

Further technical details

  • Version 2.2.0
  • Windows
@DavidZidar DavidZidar added the Type: Bug Something isn't working label Sep 8, 2024
@DavidZidar
Copy link
Author

DavidZidar commented Sep 8, 2024

I tried to get my potential fixes above to work but had no luck, I think more code is being trimmed than I first suspected.

However, I was able to figure out that adding the following line is enough to make the compiler keep the necessary code:

new OutputCacheOptions().AddPolicy("AOT workaround", (IOutputCachePolicy)null!);

@MihaZupan MihaZupan added this to the YARP 2.3 milestone Sep 10, 2024
@MihaZupan
Copy link
Member

Thanks for raising the issue. This looks like a Native AOT bug and appears to be fixed in .NET 9.
I think we should add a workaround in YARP to unblock .NET 8 though.

@MihaZupan MihaZupan removed their assignment Sep 24, 2024
@MihaZupan MihaZupan added the help wanted We will welcome a contribution label Sep 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted We will welcome a contribution Type: Bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants