DeployU
Interviews / Cloud & DevOps / Azure Functions have 10-second cold starts causing API timeouts. Optimize for low latency.

Azure Functions have 10-second cold starts causing API timeouts. Optimize for low latency.

debugging Serverless Interactive Quiz Code Examples

The Scenario

Your Azure Functions HTTP API experiences severe cold starts:

Request timeline (cold start):
├── Function host initialization: 3s
├── .NET runtime startup: 2s
├── Dependency injection setup: 3s
├── Database connection pool: 2s
└── Actual function execution: 0.5s

Total: 10.5s (API Gateway timeout: 10s)

Users see intermittent timeouts, especially after periods of inactivity.

The Challenge

Reduce cold start times to under 2 seconds while balancing cost. Understand the factors affecting startup time and implement targeted optimizations.

Wrong Approach

A junior engineer might switch to Premium plan with always-on instances set to 100, use a timer trigger to ping the function every minute, or ignore the problem and increase timeouts. These approaches are expensive, wasteful, or don't solve the actual latency issue.

Right Approach

A senior engineer analyzes cold start components, optimizes application initialization, uses Premium plan with appropriate pre-warmed instances, implements lazy loading, and considers Durable Functions for long-running operations.

Step 1: Understand Cold Start Components

Cold Start Breakdown:
┌─────────────────────────────────────────────────┐
│ Platform (Azure) - Cannot control               │
├─────────────────────────────────────────────────┤
│ • Instance allocation: ~500ms                   │
│ • Runtime initialization: ~500-1500ms           │
│ • Extension loading: ~200-500ms                 │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Application (Your code) - CAN optimize          │
├─────────────────────────────────────────────────┤
│ • Package restore/loading: ~500-2000ms          │
│ • Dependency injection: ~200-3000ms             │
│ • Static initialization: varies                 │
│ • Connection establishment: ~500-2000ms         │
└─────────────────────────────────────────────────┘

Step 2: Use Premium Plan with Pre-Warmed Instances

// Premium plan with pre-warmed instances
resource functionAppPlan 'Microsoft.Web/serverfarms@2022-09-01' = {
  name: 'func-plan-premium'
  location: location
  sku: {
    tier: 'ElasticPremium'
    name: 'EP1'
  }
  properties: {
    maximumElasticWorkerCount: 20
  }
}

resource functionApp 'Microsoft.Web/sites@2022-09-01' = {
  name: 'my-api-function'
  location: location
  kind: 'functionapp'
  properties: {
    serverFarmId: functionAppPlan.id
    siteConfig: {
      // Pre-warmed instances always ready
      preWarmedInstanceCount: 2

      // Minimum instances (never scale to zero)
      functionAppScaleLimit: 10

      // Runtime settings
      netFrameworkVersion: 'v8.0'
      use32BitWorkerProcess: false

      // Keep instances warm longer
      appSettings: [
        {
          name: 'WEBSITE_WARMUP_PATH'
          value: '/api/health'
        }
        {
          name: 'FUNCTIONS_WORKER_RUNTIME'
          value: 'dotnet-isolated'
        }
      ]
    }
  }
}

Step 3: Optimize Application Code

// BEFORE: Slow startup with synchronous initialization
public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        // Bad: Synchronous database connection
        var dbConnection = new SqlConnection(connString);
        dbConnection.Open();  // Blocks during cold start!

        // Bad: Loading all configuration upfront
        var config = LoadAllConfiguration();  // Blocks!

        builder.Services.AddSingleton(dbConnection);
    }
}

// AFTER: Lazy initialization
public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        // Good: Lazy connection factory
        builder.Services.AddSingleton<IDbConnectionFactory, LazyDbConnectionFactory>();

        // Good: Configuration loaded on-demand
        builder.Services.AddSingleton<IConfiguration>(sp =>
            new ConfigurationBuilder()
                .AddEnvironmentVariables()
                .Build());

        // Good: Use AddScoped for per-request services
        builder.Services.AddScoped<IMyService, MyService>();
    }
}

public class LazyDbConnectionFactory : IDbConnectionFactory
{
    private readonly Lazy<SqlConnection> _connection;

    public LazyDbConnectionFactory(IConfiguration config)
    {
        _connection = new Lazy<SqlConnection>(() =>
        {
            var conn = new SqlConnection(config["SqlConnectionString"]);
            conn.Open();
            return conn;
        });
    }

    public SqlConnection GetConnection() => _connection.Value;
}

Step 4: Reduce Package Size

<!-- BEFORE: Large package with unused dependencies -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
    <!-- Bad: Not trimmed -->
  </PropertyGroup>

  <ItemGroup>
    <!-- Bad: Heavy dependencies -->
    <PackageReference Include="Entity.Framework.Core" Version="8.0.0" />
    <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
  </ItemGroup>
</Project>

<!-- AFTER: Optimized package -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>

    <!-- Enable trimming -->
    <PublishTrimmed>true</PublishTrimmed>
    <TrimMode>link</TrimMode>

    <!-- Ahead-of-time compilation -->
    <PublishAot>true</PublishAot>

    <!-- Single file deployment -->
    <PublishSingleFile>true</PublishSingleFile>
  </PropertyGroup>

  <ItemGroup>
    <!-- Use lighter alternatives -->
    <PackageReference Include="Dapper" Version="2.1.24" />
    <PackageReference Include="System.Text.Json" Version="8.0.0" />
  </ItemGroup>
</Project>

Step 5: Use HTTP-Triggered Warmup

// Warmup function that initializes dependencies
[Function("Warmup")]
public async Task<HttpResponseData> Warmup(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "warmup")] HttpRequestData req)
{
    // Touch lazy-loaded services to initialize them
    _ = _dbConnectionFactory.GetConnection();
    _ = _cacheService.GetClient();

    var response = req.CreateResponse(HttpStatusCode.OK);
    await response.WriteStringAsync("Warmed up");
    return response;
}

// Configure warmup path in app settings
// WEBSITE_WARMUP_PATH = /api/warmup

Step 6: Consider Durable Functions for Long Operations

// For operations that might exceed timeout, use Durable Functions
[Function("ProcessOrder_Orchestrator")]
public async Task<OrderResult> ProcessOrderOrchestrator(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var orderId = context.GetInput<string>();

    // These run asynchronously, don't block the HTTP response
    var validation = await context.CallActivityAsync<bool>("ValidateOrder", orderId);
    var payment = await context.CallActivityAsync<PaymentResult>("ProcessPayment", orderId);
    var shipping = await context.CallActivityAsync<ShippingResult>("ScheduleShipping", orderId);

    return new OrderResult(validation, payment, shipping);
}

[Function("ProcessOrder_Http")]
public async Task<HttpResponseData> ProcessOrderHttp(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req,
    [DurableClient] DurableTaskClient client)
{
    var orderId = await req.ReadFromJsonAsync<string>();

    // Start orchestration and return immediately
    var instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
        "ProcessOrder_Orchestrator", orderId);

    // Return 202 Accepted with status URL
    return client.CreateCheckStatusResponse(req, instanceId);
}

Step 7: Use Flex Consumption Plan (Preview)

// New Flex Consumption plan offers faster cold starts
resource functionAppPlan 'Microsoft.Web/serverfarms@2023-01-01' = {
  name: 'func-plan-flex'
  location: location
  sku: {
    tier: 'FlexConsumption'
    name: 'FC1'
  }
  properties: {
    reserved: true
  }
}

// Flex benefits:
// - Faster cold starts (VNet integration without penalty)
// - Instance size options (2GB, 4GB memory)
// - Always-ready instances (like Premium but pay-per-use)
// - Concurrency control per instance

Cold Start Comparison by Plan

PlanTypical Cold StartAlways-On OptionCost
Consumption2-10+ secondsNoPay per execution
Premium EP1Under 1 secondYes (pre-warmed)~$150/month base
Premium EP2Under 1 secondYes (pre-warmed)~$300/month base
Flex Consumption1-3 secondsYes (always-ready)Pay per execution + always-ready
DedicatedNoneAlways onApp Service pricing

Cold Start Optimization Checklist

OptimizationImpactEffort
Premium plan + pre-warmedHighLow
Lazy dependency injectionHighMedium
Reduce package sizeMediumMedium
Use System.Text.JsonLowLow
Enable AOT compilationMediumMedium
Warmup functionMediumLow

Practice Question

Why is lazy initialization of database connections important for reducing Azure Functions cold starts?