My web app in Azure has key/values in application settings Environment Variables.
Question – How does Azure store these values? More specifically, when I fetch each value, is it fetched from a small memory cache or is there some internal file that gets looked through to fetch the value?
My Concern – If I start receiving lots of traffic on my site and there is a high frequency of key/value (Sendgrid, Stripe, Cloudinary) fetching from the Environment vaiables, is this going to cause a slow down in the app if the values are stored in a file vs memory?
Below are two ways I fetch these values, the first method fetches once on startup, saves the values in a cached object ‘SendGridOptions’ and the other fetches each time I need a value. The downside with the first method is if a value changes I have to restart the web app, with the second method Azure gives me a message saying it might have to restart, but my concern is if there’s a high frequency of fetching the app will slow down because it has to fetch from some internal configuration file and not memory?
- Startup.cs -> ConfigurationServices(IServiceCollection services)
// fetch all values needed once at startup
public void ConfigureServices(IServiceCollection services) {
services.Configure <SendGridOptions> (_config.GetSection("SendGrid"));
}
- Fetch value when needed in each Controller with IConfiguration.
private readonly IConfiguration _config;
public CalendarController(IConfiguration config) {
_config = config;
}
ControllerFunction() {
var secretKey = = _config["SendGrid:SecretKey"];
}
2
Answers
I don’t have insider knowledge of the Azure backend, but my understanding is that config values are loaded into memory at application startup, and they are then fetched from memory as needed. This is why the app needs to re-initialize when you change the values on disk.
If you want a method to alter your config values without restarting the app, you can point the configuration variables at another source of information, such as a database or Azure Key Vault (maybe especially appropriate for your SendGrid keys). This will introduce some minor latency, of course, but you could build a read-through memory cache for this pretty easily. A read-through cache could then be on a timer to refresh (value expiration) or through administrative signaling it can be instructed to refresh its value (by reading from Azure Key Vault again to fetch updated value).
An alternative service to think about would be Azure App Configuration, which also lets you use pointers to other values and has additional benefits for managing change as a whole (feature flags, etc). It can be especially valuable when you’re using multiple hosts for your application, letting you use one system of record for configuration values rather than setting them by hand in each service config.
https://learn.microsoft.com/en-us/azure/azure-app-configuration/overview
👨💻 Let’s consider such a setup:
⚙ Configuration in ASP.NET Core works in such a way:
Providers are implicitly configured in
ConfigurationBuilder
.By default there are few providers configured for environment variables and JSON files.
Providers can be of types:
Configuration
is built:Configuration values are sequentially loaded from every provider (order matters) in a key-value form.
Every provider has its own specific implementation and reload mechanism.
Binding and DI are configured for
IOptions<>
/IOptionsSnapshot<>
/IOptionsMonitor<>
When request is processed:
options
that can be of typesIOptions<>
/IOptionsSnapshot<>
/IOptionsMonitor<>
options
instance is constructed (see below for different types of options), it’s read fromConfiguration
. In that moment key-value pairs become an object.options
value is injected via constructoroptions.CurrentValue
Different types of
options
provide different functionality:IOptions<>
value is read fromConfiguration
only the first time.IOptionsSnapshot<>
value is bound once per request.IOptionsMonitor<>
value is cached and reloaded with some delay every time when provider notifies about changes.If configuration is changed in the source (JSON file, App Configuration, etc.), respective provider updates key-value pairs in its own bucket inside
Configuration
. It happens with a configurable delay.This happens only if provider supports reloading and reload is enabled when provider is registered in
ConfigurationBulider
on the start of the application.🕔 Latency points
So, we have only two points in time when something effectively happens with configuration values:
1️⃣ Provider loads or reloads key-value pair to memory (doesn’t impact requests):
appsettings.json
).2️⃣
IOptions<>
/IOptionsSnapshot<>
/IOptionsMonitor<>
objects are instantiated by DI:IOptions<>
– only first time; cached forever (no impact).IOptionsSnapshot<>
– on every request; cached per request (minor impact).IOptionsMonitor<>
– only ifConfiguration
was reloaded; otherwise, cached (less impact).📝 Notes
Configuration
is updated per provider only for reloadable providers. That happens in the background.IOptions<>
/IOptionsSnapshot<>
/IOptionsMonitor<>
) for your use case.IOptionsMonitor<>
,options.CurrentValue
is rebound fromConfiguration
only in case if any provider was reloaded.Conclusion
IOptionsMonitor<>
but configuration is not being reloaded by any provider, there should be no latency impact for requests.IOptionsMonitor<>
allows refreshing configuration values without restarting the application.Configuration
; if the configuration is big and changes all the time (highly unlikely).Configuration
is big and constantly reloads (highly unlikely) plusIOptionsMonitor<>
is in use.! Sidenote
🤦♀️ Injecting
IConfiguration
via constructor is a bad taste.IConfiguration
is not meant to be injected butIOptions<>
/IOptionsSnapshot<>
/IOptionsMonitor<>
are.🔗 Links: