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

Add support for app level initialization logic #586

Open
davidebbo opened this issue Aug 17, 2016 · 54 comments
Open

Add support for app level initialization logic #586

davidebbo opened this issue Aug 17, 2016 · 54 comments
Labels
Milestone

Comments

@davidebbo
Copy link
Contributor

e.g. in this thread, the user needs to call ThreadPool.SetMinThreads, and currently we have no good place to make App Domain init time calls.

Of course, that gives user potential to make some bad calls, but that's a small concern compared to the scenarios that can unblock (and the could make the same calls in a function anyway).

@mathewc
Copy link
Member

mathewc commented Aug 17, 2016

We might implement this as a special Function. E.g. the user can implement an "init" function (either by naming convention, or perhaps flagged via a metadata property. We'll invoke that function first, before starting the host.

@davidebbo
Copy link
Contributor Author

Right, that's the kind of thing I had in mind, some special function.

One interesting design issue is knowing when to call it given that this function can change while the app domain is up. Do we give user any guarantee that it can only be called once? It's probably simpler if the contract is that they need to make their init code idempotent, so that we can keep calling it on the same domain/host as needed.

@fabiocav
Copy link
Member

If we decide to call it when the host starts (as opposed the application, unless that's what Mathew means), that function could be called multiple times within the Process/AppDomain life span. We could, however, make sure that only happens once, but depending on the scenario, that may not be the desired behavior.

@mathewc
Copy link
Member

mathewc commented Aug 17, 2016

Yeah, I think the requirement is to have something that gets called when the Function App starts (I mispoke above and said "host").

@davidebbo
Copy link
Contributor Author

Yes, but that would then imply a full App Domain cycle (and not just host cycle) if the user edits it.

@christopheranderson christopheranderson added this to the backlog milestone Aug 22, 2016
@contractorwolf
Copy link

I am doing this and it kind of works like initialization. I setup db connection and schema setup after my node_module requires. I put all that code above the exported function

var mongoose = require("mongoose");
var redis = require("redis");

//gets the object json from a json file defined by the name
var offerSchema = require("./schemas/offer.js");
var offer = new mongoose.Schema(offerSchema);

//creates the model with the mongodb collection name and the schema
var OfferModel = mongoose.model('Offer', offer);

// Database setup
mongoose.connect('mongodb://xxxxxx:xxxxx@xxxxx.com:xxxx/creditcards-dev');

module.exports = function(context, req) {.....

my understanding is that the entire module would be woken up and would execute the init code (above) and then persist and continue to field additional requests (through the exported function). If the goes cold after not getting requests it would fall back asleep. Further calls would get a longer cold start when they were brought back in by requests.

I am new to Azure Functions and am just playing around but the tests I have done seem to support my theory, but I could be wrong.

@contractorwolf
Copy link

is there something wrong with my approach @davidebbo ?

@davidebbo
Copy link
Contributor Author

@contractorwolf We probably had more C# in mind in the above discussion, and we need to think about Node as well.

In your approach, I think you'll end up having this code execute each time you modify your function.

@contractorwolf
Copy link

what do you mean "each time you modify your function"? whenever I change my code? is that not where I want it to initialize?

@contractorwolf
Copy link

is there some documentation of the Azure Function lifecycle? I am imagining that there is a server that is hosting the individual functions and the first request loads your function (executing the require statements, the code inline before the export and then instantiating the export) and then holds that function up for an indeterminate amount of time (AWS was 2-5 minutes with their Lambda service). When a second request comes in if there is a live function running it can respond to the request (using that instance initialization parameters) it does, if not it starts a new one up. Please correct me if I am mistaken, I want to understand this better.

@davidebbo
Copy link
Contributor Author

what do you mean "each time you modify your function"? whenever I change my code? is that not where I want it to initialize?

Yes, I mean whenever you modify that function. And no, that is not when you want to run the initialize code, since we're talking about App Level init logic. i.e. logic that is global to all Functions in the Function App.

The lifecycle is at the level of the Function App and not the individual Functions.

@contractorwolf
Copy link

@davidebbo can you show an example of instantiating a database connection (somewhere outside the function code) that gets used in an Azure Function?

@davidebbo
Copy link
Contributor Author

We don't have good support for this right now. That's why we have this issue :)

@contractorwolf
Copy link

@davidebbo I am not understanding, is it not possible or you aren't sure if it is possible? Did Microsoft would release this as a product without proper examples or documentation?

@davidebbo
Copy link
Contributor Author

Azure Functions is still in pre-release, so there are still rough edges. We don't currently have a clean place to write app level initialization logic, hence the look for potential workarounds in the meantime.

@markanewman
Copy link

@davidebbo @mathewc Has any more thought been given to this? App level init seems like a great place to add in hooks for DI like we get with WebJob's JobHostConfiguration.JobActivator.

@erikschlegel
Copy link

Hey - just wanted to revisit the discussion to see if an app level Azure Function init hook feature is on the horizon?

@JonasGranlund
Copy link

I am also really interested in this. I have a problem with scaling on consumption plan since it always start with 4 threads (as it seems) and it is 500ms delay for each new thread to come up together with the cpu load to start it. Since I get in alot of messages in batches on a service bus Topic it really cant keep up at all. If I preload the servicebus with 10 000 messages and start the azure function it handle around 45 messages per second on Consumtion plan. But If I host it in my own App Service Plan and initiate it with 12 io- and worker- threads per instance I easily handle 1000 of the same messages per second. I think it is a very important thing to address, so we can handle high load rapidly. Would be so important to have somewhere to handle this.

@phatcher
Copy link

Any progress on this?

Without it can't easily migrate anything I have from WebJobs as they typically use a bunch of services which are configured in a container

@rhythmnewt
Copy link

Also interested if anything is on the horizon.

@SimonLuckenuik
Copy link

Only efficient way I have found so far for that scenario is to rely on some Bootstrapping.Init static method which triggers a static ctor in that same class (init once), which does the heavy lifting and delegate initialization to one or several bootstrap specific classes. That Init method is called by each and every function.

@pmaheshgupta
Copy link

could u provide any sample code? is this init method called for every instance of function execution?

@SimonLuckenuik
Copy link

Yes called for every execution of every function, something similar to that:

    public static class Bootstrapping
    {
        static Bootstrapping()
        {
            // Init here, called once because of static ctor. Assign to static members if required.
        }

        public static void Init()
        {
        }
    }

    public static class Function1
    {
        [FunctionName("Function1")]
        public static HttpResponseMessage Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "HttpTriggerCSharp/name/{name}")]HttpRequestMessage req, string name, TraceWriter log)
        {
            Bootstrapping.Init();
            return req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
        }
    }

@codymullins
Copy link

I too would like this, or at least the recommended pattern. We're doing lots of API requests using HttpClient under the covers, which according to these Microsoft antipatterns it's a bad thing to initialize this multiple times.

Is the suggested thing to do, adding a static constructor here to initialize it?

@d-dizhevsky
Copy link

d-dizhevsky commented Oct 11, 2017

@pmaheshgupta here is what I've done so far:

    public class Bootsrapper
    {
        private static IConnectionMultiplexer lazyRedisContext;

        public static IConnectionMultiplexer RedisContext => lazyRedisContext ?? CreateRedisContext();
        public static IDatabase DefaultRedisDb => RedisContext.GetDatabase();

        protected Bootsrapper() {}

        private static IConnectionMultiplexer CreateRedisContext()
        {
            return lazyRedisContext = ConnectionMultiplexer.Connect(ConfigurationHelper.RedisConnectionString);
        }
    }

And now simple inherit your functions from Bootstapper.cs and the object will be shared across the same azure function. Basically it is the same thing @SimonLuckenuik described above but you don't need to use static constructor and initialize everything in every function of your app if you don't need to.
It definitely don't work for all of your functions simply because they run on the different servers.
I know it is all the way tricky-hacky but it works and seems there is no alternative ways of doing that at the moment.

@sasolanki
Copy link

Would appreciate if we can have init function so we can use Newtonsoft json's schema library key for function app instead of registering into all functions' code.

@andriysavin
Copy link

andriysavin commented Nov 1, 2017

I want to augment described scenarios with the following. I have need to use some environment information to initialize a function/function app on start. For example, I want to use .Net Core config system and need to know base directory to specify where to look for appsettings.json, so I need ExecutionContext object. Or I'd like to take function's TraceWriter (with some wrapper or accept an ILogger) and inject it as a logger using DI.
However, these objects are only provided on function call. Worse, I can't be sure they are the same for each call (no doc on this), so I can't use just what the first call provides.
Last, I'd also like to handle app shutdown to gracefully dispose my resources (flush some loggers etc). The only idea which comes to mind is handling AppDomain unload, but I'm not sure its correct and doesn't handle host reloading.
Any advice on this?

@chadwackerman
Copy link

chadwackerman commented Dec 19, 2017

Threads like this one are the most depressing type of Microsoft issues threads. Unfortunately there are literally hundreds of them scattered amongst the more poorly managed projects.

Year one: "It's a beta product, we pushed it out early to get feedback. And hey, look at me, I'm playing on GitHub!"

Year two: "We're still thinking how to plug the obvious architectural holes and problems we created with our half-baked initial release. And wow, GitHub sure is a time sink."

Year three: Crickets.

@davidebbo You haven't responded to valid customer requests since 2016.

@surenderssm
Copy link
Member

Any updates on this ?
My two cents :as there are two Pricing plan

  1. Consumption plan - When you're using a Consumption plan, instances of the Azure Functions host are dynamically added and removed based on the number of incoming events. So having static implementation of resources to be used is not going to help always.

2.App Service plan - Dedicated VMs are allocated to your App Service apps, which means the functions host is always running. So static implementation of resources to be used is going to help

@JonasGranlund
Copy link

Hi,
I have not had the same problem anymore, when I preload a SB Queue or Topic with +10 000 messages, and increase the maxConcurrentCalls from the default=16 I can handle around 1000 messages per second now on Consumtionplan, compaired from the 45 I handled around 8 months ago. And regarding the http calls, if you do fast increase of calls you need to handle 503 errors during the seconds the consumtionplan scales out, so a retry with backoff and circuit breaker (like Polly nuget) can be a good implementation.

@ishepherd
Copy link

Consumption plan - When you're using a Consumption plan, instances of the Azure Functions host are dynamically added and removed based on the number of incoming events. So having static implementation of resources to be used is not going to help always.

@surenderssm Please give a example where this is a concern?

@darthmolen
Copy link

I too am interested in this in the sense I want to initialize HttpClient once and continually re-use if necessary but need an init location to do that. This is to prevent socket exhaustion. Do we have an ETA on this?

@mstumpfx
Copy link

Any workarounds for monitoring/reporting cold starts?
App level initialization logic seems to be the place for this. I am not sure how to report cold starts with multiple functions in an app.

@mathewc
Copy link
Member

mathewc commented Mar 27, 2018

Regarding cold start data logging - we actually did work recently to emit data to our system logs for cold start requests for our own internal analysis. This data isn't currently written to user logs though it could be, or we could emit it to AppInsights if configured.

@paulbatum
Copy link
Member

Looks like this is a dupe of #281 (or vice versa).

@sebader
Copy link
Member

sebader commented Oct 17, 2018

Is there any update on this? We now thought of a workaround with a TimerTriggered Function that has RunOnStartup = True and a CRON expression that will basically never fire.
Do you see any immediate downsides with that? I'm not entirely sure how this behaves in a consumption plan. When does "startup" occur that that would trigger the Timer?

@MisinformedDNA
Copy link

Why is this not a valid solution?

[assembly: WebJobsStartup(SomeStartupClass)]
public class SomeStartupClass : IWebJobsStartup
{
    public void Configure(IWebJobsBuilder builder)
    {
        throw new NotImplementedException();
    }
}

See here and here for more discussion.

@cmenzi
Copy link

cmenzi commented Jun 12, 2019

Hi

Is there any updates on this?

It would be really great having an extension point/hooks for this, like:

public interface IFunctionLifetime
{
	/// <summary>
	/// Called during activation.
	/// </summary>
	Task OnStartAsync();

	/// <summary>
	/// Called during deactivation.
	/// </summary>
	Task OnStopAsync();
}
public class MyFunctionLifetime : IFunctionLifetime
{
	private readonly IServiceBusManager _serviceBusManager;
	
	public MyFunctionLifetime(IServiceBusManager serviceBusManager)
	{
	    _serviceBusManager = serviceBusManager;
	}
	
	public async Task OnStartAsnyc() 
	{
	    await _serviceBusManager.CreateSubscription("FunctionInstanceId");
	}

	public async Task OnStopAsync()
	{
	    await _serviceBusManager.DeleteSubscription("FunctionInstanceId");
	}
}

Together with DependencyInjection -> Very powerfull!

It's also ok, that this is called per function instance, but at least we have the possibilty to hook into with an async interface.

@fabiocav What do you think?

@grbspltt
Copy link

Any update to this, 4 years and counting? Looking for node support to setup DB connections at startup, no matter which function is called first. Right now the work around to support this is quite poor.

@ishepherd
Copy link

@grbspltt
Copy link

@ishepherd possibly for the short term, I will give it a shot and see how that works, although it does seem like a lot more money to pay just to have code run at startup. It would be nice to have better support on this feature.

@andriysavin
Copy link

Looks like FunctionsStartup can be used for many high-level scenarios, read more here https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection

@grbspltt
Copy link

@andriysavin looking for node support. allegedly node is a first class citizen in azure functions.

@rcg-dev
Copy link

rcg-dev commented Apr 2, 2021

Still waiting for node support.

I am thinking that since this exists: https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-warmup?tabs=javascript expanding the warmup trigger to run on startup for the initial instance would basically solve this issue.

Current workaround: https://stackoverflow.com/a/67013052/12568278

@grbspltt
Copy link

grbspltt commented Apr 2, 2021

I just discovered another reason for this function on node. All my timer triggered functions that rely on DB connections have never succeeded. After some investigation it is because they all depend on DB connections.

@ForseXll
Copy link

ForseXll commented Apr 6, 2021

Yes, node support would be great. @davidebbo @mathewc

@davidebbo
Copy link
Contributor Author

@ForseXll I'm no longer involved in this project, so I'll let @mathewc or others respond.

@drekuru
Copy link

drekuru commented Jun 8, 2021

@mathewc any updates on this?

@Renetnielsen
Copy link

+1 to Node support

@ianadavies
Copy link

ianadavies commented Jun 16, 2021

Sorry if a bit late on this, but just had this issue. So this is how I got code to run on startup and when you need some dependency injection and you need it to start before any function triggers.

NB: This only works if your code is either very short lived or it can be a singleton for the lifetime of the node.

public class Startup : FunctionsStartup
    {
       
        public override void Configure(IFunctionsHostBuilder builder)
        {
            // create a new service collection -> NOT using builder.Services
            var serviceCollection = new ServiceCollection();

            // add the min DI items you need
            serviceCollection.AddMinimalItemsINeedExtension();
            
            // build the provider
            var provider = serviceCollection.BuildServiceProvider();
            
            // get the dependency
            var dependency = provider.GetService<IMyDependency>();

            // run your startup code
            dependency.RunTheStartUpCodeIWant();
            
            // either dispose of this or add the singleton to the builder
            // so your function app can access it.
            builder.Services.AddSingleton<IMyDependency>(dependency);
        }
}

Hacky I know but it works.

@drekuru
Copy link

drekuru commented Jun 16, 2021

Sorry if a bit late on this, but just had this issue. So this is how I got code to run on startup and when you need some dependency injection and you need it to start before any function triggers.

NB: This only works if your code is either very short lived or it can be a singleton for the lifetime of the node.

public class Startup : FunctionsStartup
    {
       
        public override void Configure(IFunctionsHostBuilder builder)
        {
            // create a new service collection -> NOT using builder.Services
            var serviceCollection = new ServiceCollection();

            // add the min DI items you need
            serviceCollection.AddMinimalItemsINeedExtension();
            
            // build the provider
            var provider = serviceCollection.BuildServiceProvider();
            
            // get the dependency
            var dependency = provider.GetService<IMyDependency>();

            // run your startup code
            dependency.RunTheStartUpCodeIWant();
            
            // either dispose of this or add the singleton to the builder
            // so your function app can access it.
            builder.Services.AddSingleton<IMyDependency>(dependency);
        }
}

Hacky I know but it works.

@ianadavies this is for C# functions yea?

@ianadavies
Copy link

@drekuru , Yes its c#

@drekuru
Copy link

drekuru commented Jul 5, 2022

@mathewc any updates here?

@drekuru
Copy link

drekuru commented Feb 13, 2023

@ahmelsayed any updates here? It's been over 6 years.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests