We have been dabbling with Serverless computing for sometime now . Recently we decided to check if it made sense to convert an existing customer’s WebAPI into a set of serverless functions. The accompanying question was which would be the right stack? AWS Lambda or Azure Functions.
We aim to pen down here what were our considerations while evaluating the various options and what were out discoveries/conclusions.
Existing Implementation:- The existing WebAPI tech stack was as under
- ASP.NET WebAPI2.0 in C# hosted in an Azure website
- The internal code was layered into BL, DL,Shared entities as different library projects and third party libraries
- Code First Entity Framework 6.0 with SQL Azure
- Meant to integrate with a variable ( and expanding) number of other third party APIs/system esp CRM systems
- Possibility of some async jobs like talking to NLP systems like LUIS etc to train them
We were asked to refactor the existing code so this blog post may not be relevant to “New” implementations as our decisions were influenced by “how much rework” we will need to do.
Serverless or not?? :- The first decision was whether it even makes sense to go server-less.
- Our main question to the customer was whether he sees the app as a heavy traffic app or a low traffic app. They mentioned Daily Low Traffic so it makes sense that we don’t need to book full time resources and trigger based server-less systems would reduce billing
We then looked at two main frameworks :- AWS Lambda with API gateway and Azure functions.
We were happy to see both the systems had a toolkit for VS2017 as we were keen on continuing with our existing code base and re-using as much of it as possible.
Our Migration experience with AWS Lambda using the AWS Toolkit for VS
- Keen on re-using our existing code structure we started with creating another library in our solution for the Lambda functions and adding a couple of functions to it. We then tried to connect this to our code via references
- This did not work because AWS Lambda in C# using .NET Core and NOT the complete .NET Framework. Since our existing system used the entire .NET stack we realized we will not be able to use our code as is.
- We then tried to convert our code into using .NET Core. Most of it was fine but we again got hit at the database layer as we were using Code First Entity Framework 6.0 pieces of which are again not supported by .NET Core . So we had to convert that into EF Core which was not ideal as there are some pretty important differences between Core and EF6.0
- We finally converted all our code to use .NET Core and got the references working.
- Once we were ready to start, the first thing that struck us was that when we invoked the lambda function we could not “Debug” it. We were forced to write test cases and debug the code. This is OK for basic test cases but not convenient for POST calls with complicated POCO objects or for integration testing.
- On further reading we figured that we can use some third party tools to debug locally but out of the box debugging is not possible and we need to bank on CloudWatch and log properly to understand issues.
- The next surprise was that when we invoked the function it failed ( which had worked when we tested via the test case). On further investigation it was because the SQLClient we had used had a version not supported by .NET Core. We had to downgrade that to make it work once the function was deployed.
- Next to expose these functions as APIs we had to go to the portal to configure a API Gateway to trigger the function calls from our WebApp.
- We made changes in our Angular4 web app to call the API Gateway and it all worked properly but we sorely missed the ability to put a breakpoint and debug through our local server side code and wondered how much heartache we will have when we do some more third party integrations etc.
- Another aspect which we did not particularly like was that the API names were cryptic by default (https://rsf01ssu2li3.execute-api.us-west-1.amazonaws.com/staging, https://3tf8n8bsswr0.execute-api.us-west-1.amazonaws.com/staging etc ) and it seems we need to create a certificate to give it a custom domain name http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-custom-domains.html#how-to-custom-domains-prerequisites.
Our Migration experience with Azure function using the toolkit for VS2017
Another team in parallel was looking into using Azure functions instead of AWS Lambda
- We downloaded the VS2017 tools for AzureFunctions ( https://blogs.msdn.microsoft.com/webdev/2017/05/10/azure-function-tools-for-visual-studio-2017/)
- Keen on re-using our existing code structure we started with creating another library in our solution for the Azure functions and adding a couple of functions to it. We then tried to connect this to our code via references . Everything worked well without having to make any changes as Azure functions use the full .NET stack
- We had no issues using Code First Entity Framework
- The ONLY issue we faced was that most documentation out there is as per VS2015 tools which are dated or as per working on the portal directly. Approach has changed in VS2017. Since VS2017 tools came out very recently we got stuck a few times and had to experiment a bit(Example how to enable CORS when debugging locally etc)
- We did NOT have to create any new API via portal etc as the Function defines its triggers ( for which we used HttpTriggers) at the time of creation itself .
- The best part was that we could deploy locally and put breakpoints and test .
Http Function TestActivity: http://localhost:7071/api/TestActivity
Http Function TrainNLP: http://localhost:7071/api/TrainNLP
- The API names were also not cryptic and we don’t need to create custom domains for these
- Even before publishing to Azure we could just load up these functions locally, put the endpoints in our webapp and hit the local functions which could even be debugged locally.
All in all the Azure functions app was up and running within a few hours!
We came to a few conclusions at the end of this exercise
- For the purpose of our current refactoring considering we want to re-use as much of our existing code as possible we should go with Azure functions.
- If we do decide to go with AWS Lambda for some future “from scratch” implementation we will probably not go with C#. Not having the full .NET Stack available defeats the purpose of choosing C#. Then rather go with plain js or Node.js which are treated as first class citizens by AWS.
- If for any future implementation we want to use the .NET Stack then go with Azure Functions(all other considerations staying the same).
- In our company we give a high weightage to developer convenience (read note below on what we mean by that!) . We work with VS Code, Sublime text, eclipse etc etc but we absolutely love Visual Studio as an IDE and MSFT definitely deserves bonus points for being able to debug the system live out of the box!! So unless there is a “Reason NOT TO (There are many differences in the way these two platforms actually function which may cause us to chose Lambda…and as they say …Never say Never ) we will probably prefer Azure Function over Lambda”
Again all the points mentioned in this article are wrt refactoring an existing .NET based WebAPI. Sometimes you may want to overlook the developer convenience ( although you should try really hard not to. At the same time do not confuse developer “convenience” with “familiarity” with a framework. Convenience= Tools/capability provided to the developer by the framework which help him work better !!) in-favor of a more robust architecture and future extensibility and then the decision MAY be different. Although overall the two provide the same functionalities with similar pricing, there are some fundamental differences in how Azure functions and AWS Lambda are structured and function/scale/have restrictions which at-least the lead developer/architect should know in order to architect/structure the solution properly. Another blog post about those coming soon.
Until Next time!