maxyha
maxyha

Reputation: 13

Azure Web Role warm up after Deploy

As described in various other related questions here, I am also expecting long lasting (30 seconds) first call after (re)deploying a web role with pretty large EF6 Model and a plenty of referenced nuget-packages. After trying out different proposed solutions with preloadEnabled and serviceAutoStartProviders I am still consufed and decided to reopen this topic in hope, that somebody has come upon a better solution in the meantime.

The main goal for me is to make a web role answering the first request almost as fast as subsequent calls as soon as the role quits its busy state on a fresh deployment and gets accessible by load balancer and external clients. Unfortunately I experienced following problems with proposed solutions so far:

  1. preloadEnabled:

    • I do add Application Initialization module via PKGMGR.EXE /iu:IIS-ApplicationInit in a startup task. So far so good.
    • When I then try to execute %windir%\system32\inetsrv\appcmd set site "MySiteName" -applicationDefaults.preloadEnabled:true it fails as at the time of execution of a startup script there are still no websites created in IIS on a fresh deployment.
    • If I try to set the preloadEnabled-setting via ServerManager-class in my Application_Start method instead, I cannot understand, how this code is intended to be executed before a first external call on the web role is made, as the preloadEnabled setting is defaulted to false after a fresh web role deploy and thus the Application_Start method in my understanding does not get a chance to be executed by the Application Initialization module?
  2. serviceAutostartProviders:

    • here we need to put a name of our AutostartProvider implementing the IProcessHostPreloadClient interface in applicationhost.config i.e by using either appcmd script or the ServerManager class, BUT:
      • serviceAutostartProvider is like preloadEnabled a site related setting, so we have the same problem here as with %windir%\system32\inetsrv\appcmd set site "MySiteName" -applicationDefaults.preloadEnabled:true in 1.2 - at execution time of startup script after a fresh deployment the websites are not yet created in IIS and the script fails to execute properly
      • Another possibility would be to include applicationhost.config into the deployment package, but I did not find any solution to do this for a web role.

So how did you guys managed to ensure, that preloading assemblies and some initialization code (like filling memory caches) is run before the role gets it's first hit from outside?

We start to gain some traffic now and get approx. 1-2 requests per second on our WebApi, and thus a 30 second delay for preloading "visible" to clients after each update deployment is becoming a major issue now.

We are scheduling update deploys at low traffic times, but what if I need to make an urgent hotfix deploy?

Upvotes: 1

Views: 1159

Answers (2)

maxyha
maxyha

Reputation: 13

OK. Now I got it.

The point is to set preloadEnabled-property not inside of the Application_Start method in Global.asax (as it will not be hit before a first request to the Role anyway), but inside RoleEntryPoint.OnStart.

The difference is that RoleEntryPoint.OnStart is called directly after deployment package is extracted and everything is set up for starting the role. At this moment the azure instance is still in it's busy state and is not yet available from outside as long as some code inside RoleEntryPoint.OnStart is being executed.

So here is the code I came up so far for warming up the instance before it gets its first call from outside

public class WebRole : RoleEntryPoint
{
    public override bool OnStart()
    {
        // set preloadEnabled of each Site in this deployment to true
        using (var serverManager = new ServerManager())
        {
            foreach (var mainSite in serverManager.Sites)
            {
                var mainApplication = mainSite.Applications["/"];
                mainApplication["preloadEnabled"] = true;
            }
            serverManager.CommitChanges();
        }

        // call my warmup-code which will preload caches and do some other time consuming preparation
        var localuri = new Uri(string.Format("https://{0}/api/warmup", RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["Endpoint1"].IPEndpoint));
        try
        {
            var request = (HttpWebRequest)WebRequest.Create(localuri);
            request.Method = "GET";
            var response = request.GetResponse();
        }
        catch { }

        // send this thread to sleep in order to give the warmup-request a chance to complete BEFORE the Role will get started 
        // and becomes available to the outside world
        System.Threading.Thread.Sleep(60000);


        return base.OnStart();
    }


}

Upvotes: 0

Rob Reagan
Rob Reagan

Reputation: 7686

Perhaps the best way to accomplish this is to use deployment slots. Deploy updates to your staging slot first. Before a switch from a staging slot to a production slot takes place, Kudu will hit the root of the staging slot with a request in order to warm up the application. After the request to the staging slot's root returns, the IP switch occurs and your slots are swapped.

However, sometimes you need to warm up other pages or services to get the app ready to handle traffic, and hitting the root with a warmup request is insufficient. You can update your web.config so that Kudu will hit additional pages and endpoints before the IP switch occurs and the slots are swapped.

These warmup request URLs should be in the tag. Here's an example:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <applicationInitialization>
      <add initializationPage="/pagetowarmup1" />
      <add initializationPage="/pagetowarmup2" />
      <add initializationPage="/pagetowarmup3" />
    </applicationInitialization>
  </system.webServer>
</configuration>

You can read the Kudu docs on this issue here.

Upvotes: 1

Related Questions