What Does Durable Azure Functions Solve?

Standard

Azure Functions are the way serverless compute is implemented in Azure. Amazon Web Services equivalent is called AWS Lambda. I won’t go into detail about what serverless is and what advantages it provides in this post. You can find my learnings from a project where I implemented my web api using Azure Functions here.

I will be discussing the Azure Functions V2 (Still in preview at the time of writing this blog) and in particularly the newly introduced durable functions. For a quick comparison of the V1 and V2 runtimes have a look here.

What are “Durable Functions” in Azure?

Durable functions are an extension to Azure Functions which allows you to write stateful functions. The state management is abstracted away through an async/await and allows you to write orchestration logic in code. The runtime makes sure the state is durable even if the VM running the function in restarted or the function gets recycled.

Before going into detail, let’s see what problem durable functions promises to solve. After which you will have a good understanding where this fits in solution design.

Take this simple example.

function-chaining

We have 4 functions that chain the output from the previous one as the input for the next. This type of orchestration in common in function design.

Before durable functions, there were a couple of ways of solving this.

  1. Have a seperate orchestrator function and let it handle the workflow.
    • The orchestrator functions needs to be running the entire duration of the process. This ends up costing more because we have two functions running at the same time.
    • If the orchestrator function gets recycled or the VM restarted then the current state is lost
  2. Have queues in between the functions and have the functions be triggered by a queue message.
    • Requires a lot of queues and managing the connections/triggers between functions.

When faced with the function chaining problem, I’ve generally gone with the approach of using queues in the past. Whilst it was painful to do it that way, I got the benefit of the runtime handling situations where a function was recycled and the queue message had to be replayed.

Where does durable functions fit in this scenario?

Durable functions takes away the problems we had in solution 1. The orchestrator function sleeps while waiting for a child function output and wakes up automatically once an output is received. The runtime also manages the state so we don’t have to worry about function recycling or VM restarts.

How does the code look like?


public static async Task Run(DurableOrchestrationContext ctx)
{
    try
    {
/* 

inputs - Orchestration functions support only DurableOrchestrationContext as a
parameter type. Deserialization of inputs directly in the function signature is
not supported. Code must use the GetInput method to fetch orchestrator
function inputs. These inputs must be JSON-serializable types.

outputs - Orchestration triggers support output values as well as inputs. The
return value of the function is used to assign the output value and must be
JSON-serializable. If a function returns Task or void, a null value will be
saved as the output.

*/
        var someInput = context.GetInput();

        var outputF1 = await ctx.CallActivityAsync("Function1", someInput);
        var outputF2 = await ctx.CallActivityAsync("Function2", outputF1);
        var outputF3 = await ctx.CallActivityAsync("Function3", outputF2);
        var outputF4 = await ctx.CallActivityAsync("Function4", outputF3);

        // Log output
        return outputF4;
    }
    catch (Exception)
    {
        // error handling/compensation goes here
    }
}

How does it do it? Magic?

Not really. 🙂 It uses a cloud design pattern called Event Sourcing. I’ve got a series of blog posts about event sourcing if you’re interested.

Durable functions is built on top of the Durable Task Framework. When you await the DurableOrchestrationContext, it writes to a ‘history table’ and exits the function. When an output is ready, it re-runs the function to the point of the await (checkpoint) and injects the value in. Underneath it uses queues to trigger the awakening but that’s all abstracted away from you. If you’re really interested you can have a peek inside the linked storage account for the auto/runtime generated tables and queues.

You can read more about the technology powering durable functions here.

Any Constraints?

Yes because the orchestrator functions runs multiple times (replay/checkpoint as mentioned above) it is important that the orchestrator function is deterministic. To put it simply, the code must return the same value for the same input.

  • This means you can’t generate random numbers or guids.
  • If you’re calling remote endpoints those will have to be idempotent.
  • No async/await unless you’re awaiting  on the DurableOrchestrationContext.
  • Non blocking (no I/O or Thread.Sleep).
  • Current date/time should be accessed through CurrentUTCDateTime.

Full list of constraints can be found here.

Closing Remarks

With durable functions, Microsoft has provided us with the ability to easily orchestrate our serverless functions. This will hopefully encourage more people to use Azure Functions to build their compute logic. I highly recommend you have a look at Microsoft Documentation and other use cases (i.e. Fan Out/ Fan in and Async Http) for durable functions.

If you have any thoughts or comments please leave them here as they help me improve.

Using Azure Functions HttpTrigger As Web API

Standard

If you haven’t lived under a rock for the last 18 months you would know ‘Serverless’ is the new cool kid in town. Microsoft’s offer is called Azure Functions while Amazon calls it AWS Lambda.

I won’t go into the various pros and cons of a serverless application in this blog post. There have been various posts written about the nuances of serverless you can Google for. I recommend having a read of Martin Fowler’s post here.

Some Context

I recently worked on a project at Readify that was a POC (proof of concept) mobile app in a team of 4 people. We used Ionic for the app and Azure Functions was chosen for implement the back end web API. We didn’t have a lot of surface area to expose through the API so spinning up an app service with a hosted ASP.NET WebApi project wasn’t deemed necessary. We decided to have the mobile app talk directly to the Azure Function API endpoints. We deliberated on using API Management but ultimately decided not to since it was a POC project with limited scope.

Reasons For Our Technology Choices

  • Ionic (I should do another blog post about the ‘fun’ we had setting up CI/CD using Cordova/Ionic + MacInCloud + HockeyApp)
    We had 10 weeks to develop the app and the client wanted it to be cross platform. We could have gone with a progressive web app but push notifications on iOS wouldn’t have been possible.
  • Serverless
    We had a lot of back end integration pieces that were triggered by certain events. This was a natural fit for what a consumption model of a serverless function provides. Azure functions were chosen because of the team’s experience with it.
  • Http Triggered Azure Function As Web API
    This was perhaps the most contentious choice. We could have easily gone with a full ASP.Net WebApi solution but since this was a POC app and we wanted to see if we could utilize the same consumption model for the API as well. A decision was made to separate the core part of the project as a class library (We used a command/query dispatcher pattern), so pivoting to a asp.net web api project wasn’t hard if required further down the line.
  • API Management
    We initially started with API Management provisioned in Azure but quickly found out that we can’t have it emulated locally so things like the CORS (Cross Origin Resource Sharing) functionality or authentication would not be possible to test if wanted to have the app test/debug against the azure functions running locally. This threw a big wrench in our plans. API management wasn’t cheap either. It didn’t fit with the consumption based costing model we were going with. So we decided to drop it and implement CORS and authentication ourselves.

How Did We Implement The API?

An Azure Function serverless function needs to be very lightweight. We were very careful not to introduce unnecessary complexity.

  • Most requests coming through had a JWT bearer token so we needed a way to decode and construct a proper claims principal. If there was anything wrong with the JWT (ie wrong ‘audience’ or ‘scope’) we would throw a 401 response.
  • All requests needed to support CORS.

The http request needed to go through a layer that handles authentication and another that handles CORS. In an ASP.Net WebAPI application this is handled through the OWIN pipeline. The Auth and CORS middleware inspects and handles the request appropriately and sets the HttpContext accordingly.

It’s a simple enough pattern to follow so we designed our middleware interface based on OWIN.


namespace Functions.Infrastructure
{
    public abstract class HttpMiddleware
    {
        public HttpMiddleware Next;

        protected HttpMiddleware(HttpMiddleware next)
        {
            this.Next = next;
        }

        protected HttpMiddleware()
        {

        }

        public abstract Task InvokeAsync(IHttpFunctionContext context);
    }
}

It’s a simple pattern that works like the russian Matryoshka dolls. Each middleware has the chance to create/augment the response and decide whether or not to call the next middleware. You get a chance to modify the response before and after invoking the next middleware in the pipeline.

We don’t have a HttpContext to work with like we do in WebApi so we rolled out our own.

namespace Functions.Infrastructure
{
    public interface IHttpFunctionContext
    {
        HttpRequestMessage Request { get; }
        HttpResponseMessage Response { get; set; }
        ILogger Logger { get; }
        IUser User { get; set; }
    }
}

IUser is just a ClaimsPrincipal. We could have set the Thread.CurrentPrincipal as well but .NET core doesn’t seem to set it anyway in WebAPI. So why bother right? So far so good.

Just for completeness sake here is the interface and implementation we used to create the pipeline.

namespace Functions.Infrastructure
{
    public interface IMiddlewarePipeline
    {
        void Register(HttpMiddleware middleware);
        Task<HttpResponseMessage> ExecuteAsync(IHttpFunctionContext context);
    }

    public class MiddlewarePipeline : IMiddlewarePipeline
    {
        private readonly List<HttpMiddleware> _pipeline;

        public MiddlewarePipeline(List<HttpMiddleware> pipeline)
        {
            _pipeline = new List<HttpMiddleware>();

            foreach (var httpMiddleware in pipeline)
            {
                Register(httpMiddleware);
            }
        }

        public void Register(HttpMiddleware middleware)
        {
            if (_pipeline.Any())
            {
                _pipeline[_pipeline.Count - 1].Next = middleware;
            }

            _pipeline.Add(middleware);
        }

        public async Task<HttpResponseMessage> ExecuteAsync(IHttpFunctionContext context)
        {
            try
            {
                if (_pipeline.Any())
                {
                    await _pipeline[0].InvokeAsync(context);

                    if (context.Response != null)
                    {
                        return context.Response;
                    }
                }

                throw new MiddlewarePipelineException();
            }
            catch (Exception e)
            {
                context.Logger.Error(e.Message, e);
                return context.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
            }
        }
    }
}

We just Register() the middleware we want and call the ExecuteAsync() with the FunctionContext. Couldn’t be any more simpler right? 🙂

CORS

Let’s look at an example of how we would implement CORS using our middleware now. I’ve allowed any origin here by using *. Use an appropriate value that works for you. (The CORS feature pane in your Azure Function settings might need an entry with just a * as well. The online guidance for this isn’t very clear. We found that putting one entry with a * worked for us)

namespace Functions.Infrastructure.Middleware
{
    public class CorsMiddleware : HttpMiddleware
    {
        private readonly string _allowedVerbs;

        public CorsMiddleware(string allowedVerbs) : base()
        {
            _allowedVerbs = allowedVerbs;
        }

        public override async Task InvokeAsync(IHttpFunctionContext context)
        {
            var response = context.Request.GetCorsResponse(_allowedVerbs);

            if (response == null)
            {
                await this.Next.InvokeAsync(context);

                if (context.Response != null)
                {
                    context.Response = context.Response.EnrichWithCorsOrigin();
                }
            }
            else
            {
                context.Response = response;
            }
        }
    }

    public static class CorsExtensions
    {
        public static HttpResponseMessage GetCorsResponse(this HttpRequestMessage req, string allowedHttpVerbs)
        {
            if (req.Method.Method.ToUpper() == "OPTIONS")
            {
                var response = req.CreateResponse(HttpStatusCode.OK, "Hello from the other side");

                if (req.Headers.Contains("Origin"))
                {
                    response.Headers.Add("Access-Control-Allow-Credentials", "true");
                    response.Headers.Add("Access-Control-Allow-Origin", "*");
                    response.Headers.Add("Access-Control-Allow-Methods", allowedHttpVerbs.ToUpper());
                    response.Headers.Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Your-Own-Header-Here");
                }

                return response;
            }

            return null;
        }

        public static HttpResponseMessage EnrichWithCorsOrigin(this HttpResponseMessage response)
        {
            response.Headers.Add("Access-Control-Allow-Origin", "*");
            return response;
        }
    }
}

JWT Bearer Token Authentication

Here is an simple example of how you could do bearer token authentication using this middleware concept. We used JWT.NET to implement JWT bearer tokens authentication in our TokenValidator class. I won’t include that code here as it is out of scope. Leave a comment here or email me if you get stuck implementing it.

namespace Functions.Infrastructure.Middleware
{
    public class SecurityMiddleware : HttpMiddleware
    {
        private readonly bool _mustBeAuthenticated;
        private readonly ITokenValidator _tokenValidator;

        public SecurityMiddleware(bool mustBeAuthenticated, ITokenValidator tokenValidator)
        {
            _mustBeAuthenticated = mustBeAuthenticated;
            _tokenValidator = tokenValidator;
        }

        public override async Task InvokeAsync(IHttpFunctionContext context)
        {
            var req = context.Request;
            var log = context.Logger;

            var header = req.Headers
                .FirstOrDefault(q =>
                    q.Key.Equals("Authorization") && q.Value.FirstOrDefault() != null);

            string bearerToken = header.Value == null ? string.Empty : header.Value.FirstOrDefault()?.Substring("Bearer ".Length).Trim(); ;

            if (string.IsNullOrWhiteSpace(bearerToken))
            {
                log.Warning("No bearer token provided");

                if (_mustBeAuthenticated)
                {
                    context.Response = req.CreateErrorResponse(HttpStatusCode.BadRequest, "Bearer token can't be empty");
                    return;
                }

            }
            else
            {
                context.User =  await _tokenValidator.ConstructPrincipal(bearerToken);
            }

            if (Next != null)
            {
                await Next.InvokeAsync(context);
            }
        }
    }
}

I agree it is a fair bit of boilerplate but hang in there with me. 🙂 We will get more into this topic of boilerplate in my learnings section of this post.

We’ve got Auth and CORS working but where is the middleware that actually does the handling of the API call?

Here is an example from an API call that returns the current odometer reading for a car based on a registration number in the JWT claims. Notice how we are assuming the Context.User will be populated? That’s because of the authentication middleware we used before. Is it all starting to make sense now?

public class OdometerHandler : HttpMiddleware
    {
        private readonly IGetOdometerUsingRegoQuery _getOdometerUsingRegoQuery;

        public OdometerHandler(IGetOdometerUsingRegoQuery getOdometerUsingRegoQuery)
        {
            _getOdometerUsingRegoQuery = getOdometerUsingRegoQuery;
        }

        public override async Task InvokeAsync(IHttpFunctionContext context)
        {
            var req = context.Request;
            var log = context.Logger;

            log.Info($"Odometer request received for rego:  '{context.User.Rego}'");
            var odometerDto = await _getOdometerUsingRegoQuery.ExecuteAsync(context.User.Rego);

            if (odometerDto == null)
            {
                context.Response = req.CreateResponse(HttpStatusCode.NoContent);
            }
            else
            {
                context.Response = req.CreateResponse(HttpStatusCode.OK, odometerDto);
            }

        }
    }

This is how we wired it all up. The Ioc.Bootstrap() method doesn’t do anything fancy. It just creates the required middleware instances and the FunctionContext with Request and Logger attached. Just like with OWIN, the order of middleware matters. Note how we allow the OPTIONS http verb because we want it handled within the function logic in our CORS middleware. Leave a comment here if you get stuck and need assistance.

namespace Functions
{
    public static class OdometerReading
    {
        [FunctionName("OdometerReading")]
        public static async Task<HttpResponseMessage> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "GET", "OPTIONS")]HttpRequestMessage req,
            TraceWriter log)
        {
            using (var container = Ioc.Bootstrap(req, log))
            {
                var odometerHandler = container.Resolve<OdometerHandler>();
                var corsMiddleware = container.Resolve<CorsMiddleware>(
                    new NamedParameter("allowedVerbs", "GET, OPTIONS"));
                var securityMiddleware = container.Resolve<SecurityMiddleware>();
                var errorTranslator = container.Resolve<ErrorTranslateMiddleware>();

                IHttpFunctionContext context = container.Resolve<IHttpFunctionContext>();

                var pipeline = container.Resolve<IMiddlewarePipeline>(
                    new NamedParameter("pipeline",
                    new List<HttpMiddleware>(){
                      corsMiddleware,
                      errorTranslator,
                      securityMiddleware,
                      odometerHandler
                }));

                return await pipeline.Execute(context);
            }
        }
    }
}

Our Learnings & Was All That Worth The Effort?

That is the million dollar question. After all we could have gone with ASP.NET WebApi and got most of the boilerplate out of the box. Yes I do mean IOC here as well.

We wanted to challenge ourselves to see if we could do this POC app using a pay for consumption model. AppService hosted WebApi doesn’t fit that mould entirely. Nor does Azure Api Management yet. This biggest issue with API Management though is that tooling isn’t there for developers to emulate it when running your azure function locally against the mobile/web app. For a project that has many API endpoints, there is no easy way to setup API management easily. (ie We can use Swashbuckle when using ASP.NET WebApi to get a swagger file and import in API management but no such feature exists for Azure Functions http triggers).

What about AWS Lambda?

AWS Lambda supports hosting ASP.NET WebApi projects as a severless app. I haven’t used this feature before but we would have chosen that option if it were available for us in Azure Functions. It solves the problem of boilerplate. It’s also interesting to note that at the time of development of this app Azure Functions didn’t support .NET core but Lambda did.


Conclusions (Confused?)

Don’t be. We went with the approach of serverless consumption model first but ended up having to implement our own OWIN like middleware and do auth and CORS our selves. It didn’t add any significant startup or performance cost to the app but if the project boilerplate was any larger it could have.

Before you start to implement a web api using Azure Functions http triggers consider the following

  • Do you need authentication and authorization?
    If you have a lot of bespoke requirements around this I would recommend going with ASP.NET WebApi as it is more mature and has the boilerplate to handle them. Azure Functions can be secured using Azure AD but I haven’t used that feature.
  • Do you have to worry about CORS? Yes you do.
    Microsoft has changed the way CORS rules work in the platform settings in Azure Functions. Make sure the rules set work as you intended. I can’t stress this enough. Even if this is the case you will have to do some magic (implement your own CORS logic) in the functions app to handle situations where you are running it locally against the mobile/web app.
  • Do your APIs need to scale? Do you want to worry about load balancing and availability?
    You can’t beat the scalability aspects of serverless apps. You do sacrifice certain things for that luxury though. (ie Cold starts)
  • Do you need to have other developers use your API? Need rate control?
    If you do then API Management is a must. API Management also allows you to authenticate users using Azure AD too.
  • Microservices you say
    Yes this is where the serverless paradigm shines through. Be careful though. Azure functions work great with its bindings/triggers but they are geared towards cloud platforms. If you need to write to a SQL database, or worse call a SOAP endpoint you will have to do it the old fashioned way. We ended up using TableStorage, Storage Queues and Blobs. Any additional integration points were implemented using Logic Apps. This is a great combination you can use.

Final Words

For most of us this was the first time we built a mobile app paired with a serverless web api. I learned a lot about the capabilities of Azure Functions and areas where ASP.NET WebApi outshines it. Your mileage may vary but keep these considerations and experiences in your mind then next time you are presented with the opportunity to implement a web api using Azure Functions or AWS Lambda.

Thoughts? Comments? Please let me know below.