Microsoft Azure is a fantastic platform for building highly scalable software solutions. As with any software solution, the associated operational costs remain long after development has completed. Keeping the operational costs of the solution low may make the difference between success and failure! Most of these large-scale solutions have many different instances operating in a “server” mode. If these instances are written in .NET, you may realize increased density by using server Garbage Collection mode. Increased density typically reduces overall operational costs in a pay for what you consume environment. This post will discuss how to change the Garbage Collector mode used by an Azure Worker Role. We will not discuss the merits of one versus the other. This article will only focus on the Worker Role. However, the same concepts and techniques may be used to change the Web Roles mode by changing WaWebHost.exe.config instead of WaWorkerHost.exe.config.

To demonstrate the significant impact choosing the best Garbage Collection algorithm for your application can have on its performance I have included two graphs.  These two graphs were taken from a sampling of performance counters during load testing of an application, one in server mode and the other in workstation. 

Workstation Garbage Collection

Server Garbage Collection

 

How to Configure the Role

In traditional .NET development, you adjust the Garbage Collector to operate in server mode by modifying the app.config file. However, in the Worker Role, the WaWorkerHost.exe process hosts your code. Because of this hosting model, the actual configuration file you must modify is WaWorkerHost.exe.config. This file is in the %approot%\base\x64 directory.

At first look, it is tempting to just simply overwrite the configuration file with a new version. However, because of the way Azure creates and deploys the WaWorkerHost.exe binary, it is best to alter the configuration file in a way that accommodates future modifications. For this reason, your code should first check for the existence of WaWorkerHost.exe.config and only create a new one if one does not already exist.

This solution has three key parts:

  1. A PowerShell script. It will use XML parsing to adjust the existing values of the configuration file or create a new file if it does not exist.
  2. A command file. It will read the environment variables and launch the PowerShell script with an unrestricted execution policy.
  3. The start-up task. It will be added to the service configuration file and execute as a simple elevated task.

In the next few sections, we will review the solution starting with the PowerShell script, then the command file, and finally, the start-up task.

PowerShell Script

This script will check for the existence of the configuration file. It will create the file, if it does not exist, or just add or update the appropriate elements if it does exist. The settings for server mode and background mode will be passed to the script as the boolean parameters serverGC and backgroundGC.

You can define the directory of the configuration file using environment variables. Doing so will mitigate the chance that a drive other than “E:” will be assigned to the role root. [string]$configFilePath = "$(${env:RoleRoot})\base\x64\WaWorkerHost.exe.config"

The following PowerShell script uses various supporting functions included in the attached sample files, but we will not discuss them in detail. The basic flow of the configuration file adjustment is as follows:

# Create the document if required
Create-ConfigFileIfNotExists

# Load the configuration file into the XML document
[System.Xml.XmlDocument]$configurationDocument = Load-ConfigFile

if($configurationDocument -ne $null)
{
    if(Create-ElementStructureIfNotExists $configurationDocument)
    {
        # All of the entries are on the runtime element       
        [System.Xml.XmlElement]$runtimeElement = $configurationDocument.DocumentElement.SelectSingleNode('./runtime')

        if($runtimeElement -ne $null)
        {
            # Set the Server GC to enabled if requested
            [System.Xml.XmlElement]$serverGCElement = Append-ElementIfNotExists $configurationDocument $runtimeElement "gcServer"
            $serverGCElement.SetAttribute("enabled", $serverGC.ToString([System.Globalization.CultureInfo]::InvariantCulture).ToLower())

            # Set the concurrent GC to enabled if requested
            [System.Xml.XmlElement]$concurrentGCElement = Append-ElementIfNotExists $configurationDocument $runtimeElement "gcConcurrent" 
            $concurrentGCElement.SetAttribute("enabled", $backgroundGC.ToString([System.Globalization.CultureInfo]::InvariantCulture).ToLower())
        }
    }

    # Save the document
    $configurationDocument.Save($configFilePath)
}

Command File

To minimize the changes to the PowerShell scripts over time, it is easy to implement a command file. This file will act as an abstraction for the environment variables and launch the PowerShell script with an unrestricted execution policy.

The command file will check if Microsoft Azure Emulator is running. If it is running, the command file will not run the PowerShell script. The command file will also set the UserServerGC and UseBackgroundGC environment variables to default values if they were not provided. 

REM Check if the script is running in the Azure Emulator, and if so, do not run
IF "%IsEmulated%"=="true" goto :EOF
If "%UseServerGC%"=="False" GOTO :ValidateBackground
If "%UseServerGC%"=="0" GOTO :ValidateBackground

SET UseServerGC="True"

:ValidateBackground
If "%UseBackgroundGC%"=="False" GOTO :CommandExecution
If "%UseBackgroundGC%"=="0" GOTO :CommandExecution
SET UseBackgroundGC="True"

The portion of the command file that executes the PowerShell script is as follows:

:CommandExecution
PowerShell.exe -executionpolicy unrestricted -command ".\GCSettingsManagement.ps1" -serverGC %UseServerGC% -backgroundGC %UseBackgroundGC%

Exit /b

Start-up Task Definition

You must define the command file as a start-up task in the service definition file. You must also configure the appropriate environment variables. It is important to note that this task runs in elevated mode, but it will not cause the Worker Role code itself to elevate.

You can add this simple mode start-up task definition to the service configuration file for the Worker Role. You must configure the following environment variables in the start-up task definition to make this script function properly:

    IsEmulated: true if the start-up task is running in the emulator. If true, the PoweShell script will not run.
    UseServerGC: true if Worker Role should use server mode garbage collection.
    UseBackgroundGC: true if the Worker Role should use background garbage collection.

The start-up task configuration is as follows:

    <Startup>
      <Task commandLine="ServerGC.cmd" executionContext="elevated" taskType="simple">
        <Environment>
          <Variable name="UseServerGC" value="True" />
          <Variable name="UseBackgroundGC" value="True" />
        </Environment>
      </Task>
    </Startup>

Adding the Files to the Solution

Add the command and PowerShell files to the Worker Role that will be elevated. Place both files in the root directory of the Worker Role. Set the “Build Action” to “Content”, and set “Copy to Output Directory” to “Copy Always”.

Conclusion

The ability to run your Microsoft Azure Worker Role using server Garbage Collection mode is very important. Using garbage collection can improve performance and density while reducing operational costs.