Remote Debugging for Azure Functions Can Be a Breeze

Debugging Azure Functions with Ozcode
Serverless architectures are gaining traction in the software industry, and it wouldn’t be surprising to see them rise on a similar curve that we have seen with microservices. But the ephemeral nature of these short-lived units of execution makes it very difficult to debug them. How do you debug code that only throws an exception under very special circumstances, and then disappears?

Debugging Azure Functions can be a huge challenge if you don’t have the right tools. This post is based on a webinar I presented where I first introduce Azure Functions and then show how Ozcode Production Debugger overcomes the challenges of debugging them. To view the full webinar, scroll down to the end of the post.

Beyond microservices

Serverless computing has been around for about ten  years and has taken the benefits of scalability, robustness, and decoupling functionality introduced by microservices to the next level. It first became widely available when Amazon introduced AWS Lambda in 2014, followed by Google Cloud Functions and Azure Functions in 2016.

If the shoe fits…

One of the main benefits of this technology is that it is very scalable and can closely match the allocation of resources to usage, so you don’t need to anticipate and provision for usage spikes. The platform automatically scales on-demand, effectively to infinity, and then scales back down when usage subsides. The diagram below compares how resources are allocated in traditional cloud architectures compared to serverless.

Resource Usage in Traditional Architectures
Resource Usage in Serverless Architectures

With a billing model that is based entirely on execution, serverless takes the concept of “Pay Per Use” to the extreme and can make it very cheap to run applications and services on the public cloud. Take, for example, the HaveIBeenPwned website, owned by Microsoft MRD and MVP, Troy Hunt, which handles 20 million requests per month, but only costs about $30 per month to run.

But, while serverless computing is very cost-effective for the right type of application, one that utilizes relatively simple, short-lived functionality, it does place some limitations:

  • There’s a limit to how long serverless functions can run.
  • They can be costly if you continuously fire them up and run them to their limits.
  • They’re not well suited for complex business functions.

Introducing Azure Functions

Azure Functions is the serverless compute service on Microsoft Azure. Its pricing is tied to the amount of memory your application uses, and the amount of time it executes (which translates to units of Gigabyte-seconds of execution – GB-s), so clearly, you should optimize your Azure Functions-based application for those factors (which is a good practice anyway). For example, .NET Core 3.1 is much faster than previous versions, so that would be a good candidate on which to develop your serverless application. The first 400,000 GB/s and 1 million executions per month are free. After that, it’s $0.000016/GB-s and $0.20 per million executions, so it’s easy to see how websites showing activity in discrete short-lived surges can be very cheap to run.

There are three runtime versions of Azure Functions currently available, with the latest offering support for C# and F# on .NET Core 3.1, JavaScript with Node 10 and 12, Java 8, PowerShell Core 6, Python 3.6 – 3.8 and TypeScript.

Triggers and Bindings

Azure Functions is an event-driven platform and supports a variety of triggers through which you can fire up functions. For example, HTTP lets you invoke a function through an HTTP endpoint, just like calling a REST API. Or, suppose you wanted to resize every image uploaded to your applications to optimize blob storage. For that, you could use a blob trigger.  Bindings let you connect a variety of different resources to a function. There are both input bindings and output bindings through which your functions can receive and send data.  For example, for your function to send a Twilio message,  you don’t have to create a new Twilio client and tie it in with credentials. You can just set up a Twilio output binding, which lets you call into Twilio very easily and send out a message. You can learn more about Azure Functions Triggers and Bindings in the Microsoft documentation.

Web security for Azure Functions

Azure Functions offers three levels of security for integration with web APIs. The first level is for anonymous functions and is like a public API, which means anyone can use them without the need for authentication. Then there’s Function level authentication, which involves keys that pass back and forth between a caller and a function. At the highest level of security, Azure Functions lets you set up authentication via a serverless endpoint of Azure Active Directory (AAD) using JSON Web Tokens (JWT). For example, if you have a function written in React, AAD can manage authentication, scaling and then returns the JWT, which is now passed on to your call in Azure Functions. Your function can now decode and validate the JWT against AAD.

Durable Functions

Durable functions are a layer on top of Azure Functions that let you build complex workflows for reliable execution by managing state, retries, and delegation. Some of the typical application patterns which can be implemented using durable functions are:

  • Chaining – executing a sequence of functions in a specific order. For example, you could split up a function into a chain of calls and scale them independently, so even if one instance of a link in the chain goes down, it will not break the whole function.
  • Retries – retrying a request after a transient failure. This pattern is very common in messaging systems. If a message delivery fails, you retry after say, 20 s, then after 40 s, etc. until delivery finally succeeds or times out.
  • Timeout –there are many uses for timeouts. Two of the more common ones are either as reminders in business processes (e.g., if a user fills their eShopping cart, but doesn’t check out), or to retry a 3rd party service call if the service does not respond.
  • Fan out – lets you run tasks in parallel. For example, if an API returns a list of objects, you could run the same manipulation on them simultaneously.
  • External interaction – in this pattern, the application pauses execution in one of its states, waits for input, and then resumes execution. For example, a deployment might require the approval of a manager before being completed. You might think this kind of implementation does not sit well with the fact that Azure Functions are billed by execution time, but when using durable functions, you are not billed for the time that a function sleeps while it is waiting for input.

Remote debugging for an Azure web application

Let’s see an example of deploying Azure functions and then debugging them using Ozcode Production Debugger. A Vehicle Identification Number (VIN) number is a 17-digit sequence of characters that identifies a vehicle by year, make, model, and a few other attributes. In this example, we’ll use Azure Functions to run a VIN through a few steps to decode how large the corresponding vehicle is. This is a good application of Azure Functions since it is quick to run and does not involve many potentially time-consuming dependencies like networks or databases.

Creating and deploying an application to Azure Functions

We’ll start by creating a new Azure Function in Visual Studio. Since this is the latest version of Visual Studio, we’ll create a Functions version 3 application. 

Create Azure Functions Project - Ozcode

Functions are kind enough to prompt you for what sort of function you’d like to template out. Pick HttpTrigger and set the authentication to Anonymous.

Create HTTP Trigger Function - Ozcode

You should now have an empty functions project. We’re going to use two APIs from the National Highway Traffic and Safety Administration (NHTSA) for our function. The first one to decode the VIN and the second to get dimensions of the corresponding vehicle. They are both documented here: https://vpic.nhtsa.dot.gov/api/.

You can grab the source files for the objects to deserialize from our repo (https://github.com/stimms/FunctionDebugging/blob/master/FunctionDebugging/DecodedVin.cs and https://github.com/stimms/FunctionDebugging/blob/master/FunctionDebugging/DecodedVehicleSpec.cs), or you can use the little-known Paste as Class function in Visual Studio. 

Next, clean out the code in your HTTP trigger and replace it with: 

const string OVERALL_LENGTH = "OL";
        const string OVERALL_WIDTH = "OW";
        const string OVERALL_HEIGHT = "OH";

        static HttpClient client = new HttpClient();
        [FunctionName("DecodeVIN")]
        public static async Task<IActionResult> Decode(
            [HttpTrigger(AuthorizationLevel.Function, "get", Route = "decode/{vin}")] HttpRequest req, String vin,
            ILogger log)
        {
            log.LogInformation(vin);

            string vinDecodeUrl = $"https://vpic.nhtsa.dot.gov/api/vehicles/decodevinvaluesextended/{vin}?format=json";
            var result = await client.GetStringAsync(vinDecodeUrl);
            var envelope = System.Text.Json.JsonSerializer.Deserialize<DecodedVinEnvelope>(result);
            var decodedVin = envelope.Results.FirstOrDefault();

            string vehicleSpecUrl = $"https://vpic.nhtsa.dot.gov/api/vehicles/GetCanadianVehicleSpecifications/?Year={decodedVin.ModelYear}&Make={decodedVin.Make}&Model={decodedVin.Model}&units=&format=json";
            var vehicleSize = await client.GetStringAsync(vehicleSpecUrl);
            var specEnvelope = System.Text.Json.JsonSerializer.Deserialize<DecodedVehicleSpecEnvelope>(vehicleSize);
            var length = Int32.Parse(specEnvelope.Results.First().Specs.Single(x => x.Name == OVERALL_LENGTH).Value);
            var width =  Int32.Parse(specEnvelope.Results.First().Specs.Single(x => x.Name == OVERALL_WIDTH).Value);
            var height = Int32.Parse(specEnvelope.Results.First().Specs.Single(x => x.Name == OVERALL_HEIGHT).Value);

            return new OkObjectResult(length * width * height);
        }

You should now be able to hit F5 and try out the function locally by passing in a VIN. Try this one for an Acura JH4KA3261JC024072. Perfect!

Now let’s deploy the function to Azure. For expediency, we’ll publish with a right-click, but this technique should not be used for real applications. Select an Azure App Service plan.

Select Service Plan - Ozcode

Currently, Ozcode Production Debugger doesn’t work against Consumption Plans, but that is something we’re exploring. Create a new App Service Plan in your subscription and deploy the app to it. You will need an Azure Functions Premium plan for this capability.

New App Service

Remotely debugging the app on Azure with Ozcode Production Debugger

I showed you how to install Ozcode Production Debugger on Azure in a previous post. Let’s do that now.

If we now invoke our function with Acura VIN from above, it should work fine. Go ahead and try. But now try it with a different VIN like this Tesla: 5YJRE11B081000394. You’ll see that you get back a 500 error, which is not too helpful. Let’s debug that on Azure with Ozcode Production Debugger. You’ll see what the issue is right away:

There are no results for this particular VIN. An easy fix for that is to catch the exception and return a helpful error message or inserting a default value.

Debugging the ephemeral on Azure

There are many ways to deploy an application to Azure. You could put your app on a Virtual Machine, or even a cluster of redundant Virtual Machines. You could run it in App Services or Azure Batch. You could use Service Fabric, a Kubernetes cluster, or even deploy it right to an Azure Container Instance. Each of these options has its advantages and disadvantages which need to be considered and balanced before choosing the right deployment model. In this post, I introduced Azure Functions as another way for you to deploy applications and provided some of the criteria that would make Azure Functions the right choice. It would be a shame if the benefits of serverless architectures were negated because you were constantly chasing after elusive bugs. But that doesn’t have to be the case. Now, the inherently ephemeral nature of serverless in general, and Azure Functions, in particular, does not have to be an impediment to effective debugging. Ozcode Production Debugger takes all that pain away.

https://youtu.be/U4L7lAHiflg

Simon Timms

Comments

Keep your code in good shape!

Subscribe to our blog and follow the latest news in the .NET debugging industry

Ready to Dive into Your Prod Code?

Easy debugging with full time-travel data

Share on facebook
Share on twitter

Recent Posts

Follow Us

Join Ozcode YouTube Channel

Let’s start debugging, it’s free!

Ozcode Logo

This website uses cookies to ensure you get the best experience on our website.