Microsoft Azure: Function Apps Performance Considerations
Each organization has a unique journey to the cloud based on its own starting point, its history, its culture, and its goals. Development of cloud based projects and specifically working with Azure Function Apps we often get asked questions in relation to performance issues where we see the rate of message processing or the rate at which API calls are being served is not even near as expected in terms of response time.
Before we start digging into these issues I would like to make clear that no service/product is perfect specially when its at early v2.x and for the product to grow and mature this iteration process is key for improvement.
I have worked on data transport technology since the the days of working with Sybase iAnywhere Solutions (now SAP). The popular technology back then was MobiLink (data synchronisation) and Ultralite DB (remote database). I can say that in essence the fundamentals of core backbone principles of data movement still remains pretty much the same i.e. we have some data and datasource from somewhere and needs to be sent to requesting client and this needs to happen as fast and clean as possible. This data exchange line must be optimised and push the envelope of limitation based on the transport protocol.
Assuming that you have some of the basic knowledge about the Azure Function App.
Azure Functions is a solution for easily running small pieces of code, or "functions," in the cloud. You can write just the code you need for the problem at hand, without worrying about a whole application or the infrastructure to run it. Functions can make development even more productive, and you can use your development language of choice, such as C#, F#, Node.js, Java, or PHP. Pay only for the time your code runs and trust Azure to scale as needed. Azure Functions lets you develop serverless applications on Microsoft Azure (similar to AWS Lambda).
Let’s discuss the commonly faced Performance Issues one by one.
When we talk about performance it’s commonly about, but not limited to,
High CPU Consumption
High Memory Consumption
Port/ Outbound Socket Consumption
No. of threads being spawned up
No of pending requests in the HTTP Pipeline
While creating a Function App you must keep in mind the fact that moving from Consumption Plan to App Service Plan and vice-versa is not possible once the function is created. However, if you delete the Function app and recreate it on the other type of hosting plan.
Executions
Functions are billed based on total number of requested executions each month for all functions. Executions are counted each time a function is executed in response to an event, triggered by a binding. The first million executions are included for free each month.
Resource consumption
Functions are billed based on observed resource consumption measured in gigabyte seconds (GB-s). Observed resource consumption is calculated by multiplying average memory size in gigabytes by the time in milliseconds it takes to execute the function. Memory used by a function is measured by rounding up to the nearest 128 MB, up to the maximum memory size of 1,536 MB, with execution time calculated by rounding up to the nearest 1 ms. The minimum execution time and memory for a single function execution is 100 ms and 128 mb respectively. Functions pricing includes a monthly free grant of 400,000 GB-s.
Azure Functions can be used with Azure IoT Edge for no charge.
Consumption plan takes care of scaling out whenever there is a need. To describe in simple terms scaling happens when the current processing /output <= the current input. Scaling (scale out) happens when this equation doesn’t hold true. All these performance issues usually we face on Consumption plan where we don’t have the control over scaling up or scaling out the hosting plan. In addition to this, the VM specs are pretty much limited to the consumption plan. Up to 1.5 GB Ram and 1 core CPU is what we get in one instance where the Function app is running, which is not too great I would say. The other limits are:
We should design and develop our function app in a way avoiding performing an intensive tasks taking high CPU, High Memory, spawning large no of threads, opening large no of outbound socket connections etc.
Once the application usage gets past to these limits the processing rate of messages gets reduced and then the scale controller adds a new instance of a machine and the load gets distributed to keep up the processing rate.
However, it takes at least 10 seconds to add a new instance and then the load balancing of the events starts.
Especially with HTTP triggers, it takes a bit longer to add more instances. So if we are running load tests we may see a little bit higher time taken by the requests to complete as they all start to go on a single instance and we can observe the HTTP pipeline growing up initially. However when the new instances are added then processing speeds up and within 20-30 Min of time, the high load(10K requests) will be settled.
This was about the Consumption plan.
In case we use the app service plan then definitely we are choosing the VM specs we want our application to run on. So with features like auto-scaling, we can have the control on when to trigger scaling and also what size of VM to choose. Also for plans on Standard and above there is an option to use Traffic Manager as well to distribute the load.
This was about the VM and how to manage or effectively utilize the VM resources.
While building the application one should also consider the connections to other services internal or external to Azure which the Function app is going to connect while processing. The limit for no of outbound socket connections alive at any given point in time is 300 on Consumption plan. It seems to be pretty low. However, scale-out happens (indirectly) when this limit is reached.
Let's see how this works.
Suppose there is a Function app (Consumption Plan) running on one instance and the Function app itself is making 400 connections to any other service like SQL, COSMOS DB. For one user request to the Function App, there are 400 connections being made to the external service. In this case, the auto scale-out which happens on consumption plan will not help as all those outgoing connections are coming from same worker process and same instance of the worker process. Now imagine after the scale out happens, each instance will have its own worker process and each worker process will make 400 calls to the external service and every worker process in every instance will start failing with the same issue.
The consumption plan scaling will work in a scenario where there are 400 requests to the Function app itself and every function call making one connection to external service. So if initially we have one instance to start with it will get 400 calls then as it will not be able to process more than 300 calls (it will make 300 connections to the external service and will consume all 300 ports) the consumption plan scaling will happen and will add a new instance to serve rest of the calls and balance out the further load.
Along with these considerations, we can implement concurrent/parallel processing of messages by using respective trigger properties in HOST.JSON file of the Function app. These are:
While connecting to the external services it’s always advisable to use connection pooling. However, that also depends on what scenario you are working on.
So the considerations discussed above, the concurrent processing properties and connection pooling combined together can significantly improve the function app performance.
Before ending the blog couple of important points I want to share here which are worth keeping in mind when we are comparing the performance of Function app on Consumption plan vs on App Service plan Vs the same code in a Web App on a VM
- Compute power of all the three kinds of VM’s are different.
- If we are running the same code on the Function app, app service plan and in an application on VM with the similar configuration like RAM and CPU, there will be a difference in computing power observed as the underlying VM’s can be of different series. Web apps use A Series VM which are for moderate loads. When we are choosing the actual VM, we can take it of any series we want and usually we end up in choosing D series VM having high computing power. Here is an article to go through all the series of VM’s available on Azure.
#Azure Function App #Performance #Azure Functions #HTTP Trigger #Performance #Http #Trigger Performance #Azure Cloud Consulting
Thanks Mr Gecko ??