-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Description
When passing a factory method - that might throw a runtime exception - as a parameter to .GetOrAdd() on an instance of OptionsCache the cache will never recover if an exception gets thrown in the factory method.
This happens because the underlying Lazy will never try to reinitialize its value even if the error condition goes away.
For example Microsoft.Identity.Web does make an remote HTTP call during initialization of JwtBearerOptions. If theres a connection outage, when the factory method is executed, every subsequent cache access will throw an exception.
The only fix is to manually clear the cache or restart the application.
Sample:
using System;
using Microsoft.Extensions.Options; // Microsoft.Extensions.Options @ 3.1.7
namespace ConsoleApp
{
public static class Program
{
static void Main()
{
var optionsCache = new OptionsCache<Options>();
try
{
var expectedToFail = optionsCache.GetOrAdd("options", () => new Options());
}
catch { }
var shouldBeSucceeding = optionsCache.GetOrAdd("options", () => new Options());
}
private class Options
{
private static bool _throws = true;
public Options()
{
if (_throws)
{
_throws = false;
throw new Exception();
}
}
}
}
}Possible Solutions
a) change the Lazy's LazyThreadSafetyMode to PublicationOnly to allow multiple evaluations until one succeeds (this might have unintended performance consequences)
b) reset the cache entry when an exception is thrown during initialization of the Lazy
Configuration
- Which version of .NET is the code running on?
3.1.7 - What OS and version, and what distro if applicable?
Windows 10 2004 Build 19041.450 - What is the architecture (x64, x86, ARM, ARM64)?
x64 - Do you know whether it is specific to that configuration?
It is not specific to configuration