A not entirely fair comparison between alpha-release Google Cloud Functions and mature AWS Lambda: My insights into the game-changing future of serverless clouds.

Serverless computing lands on Google Cloud: Welcome to Google Cloud Functions

Update: The open beta of Google Cloud Functions was launched in March 2017. The comparison table has been updated accordingly.

The alpha release of Google Cloud Functions was officially launched in February 2016 as part of the Google Cloud Platform solution. This new cloud service aims at relieving most of the pain caused by server maintenance, deployments, and scalability. It perfectly aligns with the serverless revolution started by AWS Lambda back in 2014.

“Serverless” means that you can focus on your application logic without dealing with infrastructure at all (almost). Painless development, deployment, and maintenance of a web API is still not turn-key, although modern web application frameworks have improved dramatically in the last 5 years. Serverless computing is definitely a game changer. The event-driven approach combined with the rich cloud ecosystem offered by main cloud vendors AWS, Microsoft Azure, and Google Cloud Platform offers endless possibilities.

In this post, I would like to discuss the upcoming features of Google Cloud Functions and compare them with the current status of AWS Lambda. I’ll provide some basic examples of how you’ll be able to migrate and then test your Lambda functions on Google within minutes. I  also want to explore what serverless computing may look like in just a few months.

Google Cloud Functions & AWS Lambda

First of all, I have to admit that comparing an alpha release with a two-year-old stable product is not completely fair. That said, I believe that some of the functionalities already offered by Google Cloud Functions will make a substantial positive difference, especially from a development point of view.

Here is a quick recap of the main functionalities of both products:

Functionality AWS Lambda Cloud Functions
Scalability & availability Automatic scaling (transparent) Automatic scaling
Max. # of functions Unlimited functions 1000 functions per project
Concurrent executions 1000 parallel executions per account per region (default safety throttle) 400 parallel executions (per function, soft limit)
Max. execution time 300 seconds (5 minutes) 540 seconds (9 minutes)
Supported Languages  JavaScript, Java, C# and Python Only Javascript
Dependencies Deployment Packages npm package.json
Deployments Only ZIP upload (to Lambda or S3) ZIP upload, Cloud Storage, or Cloud Source Repositories
Versioning Versions and aliases Cloud Source branch/tag
Event-driven Event Sources (S3, SNS, SES, DynamoDB, Kinesis, CloudWatch) Cloud Pub/Sub or Cloud Storage Object Change Notifications
HTTP(S) invocation API Gateway HTTP trigger
Logging  CloudWatch Logs Stackdriver Logging
Monitoring  CloudWatch and X-Ray Stackdriver Monitoring
In-browser code editor Only if you don’t have dependencies Only with Cloud Source Repositories
Granular IAM IAM roles IAM roles
Pricing 1M requests for free, then $0.20/1M invocations, plus $0.00001667/GB-sec 1M requests for free, then $0.40/1M invocations, plus $0.00000231/GB-sec

Let’s dig deeper into each functionality.

Scalability, availability, and resource limits

Of course, this is the primary focus of both services. The key feature promises that you no longer need to worry about maintenance, downtime, or bottlenecks.

As far as AWS Lambda, scalability is completely and transparently handled by the system, which means that you don’t know how many instances or machines your functions are running on at a given time. You can monitor the usage of your Lambda functions anytime, but your visibility of the underlying architecture is limited.

On the other extreme, Google Cloud Functions explicitly creates a set of instances in the cloud. In this way, you can always check the number of machines created and monitor the load of your cluster.

In addition to scaling and monitoring, AWS Lambda has other limitations. For example, you can create an unlimited number of functions, but each execution cannot exceed five minutes (it used to be much shorter!) and you are limited to 100 parallel executions per account per region as a default safety throttle, which can be increased upon request. Furthermore, your zipped deployment packages can’t exceed 50MB (250MB when uncompressed). There are a few more AWS Lambda Limits, but I think they actually affect only very specific scenarios, so I won’t mention them here.

On the other hand, Google Cloud Functions doesn’t seem to impose such limitations (yet?), in spite of having a hard limit of 20 functions per project. I would expect this limit to eventually disappear.

Supported languages and dependency management

The first version of Lambda only supported JavaScript, and later it included Java (Jun 2015) and Python (Oct 2015). Currently, you can even write your functions in Ruby (with JRuby), or any unsupported language by running arbitrary executables (i.e. by spawning child processes).

Google Cloud Functions currently supports only JavaScript. Although there seems to be no public roadmap, I would expect Python and Java to be supported sometimes later this year.

As far as dependency management and deployment, the only real weakness of AWS Lambda is its Deployment Packages. In practice, you can use external dependencies only by including them within your zipped source code, and I find this inconvenient for many reasons. First, you are forced to compile and install these external packages on the same OS used by AWS Lambda internally. After this, every time you need to change something in your own code, you have to upload it all together. Second, this is not the way modern dependency management works. Web developers are now used to declaring and versioning their code dependencies, rather than providing local compiled libraries.

Of course, the whole process can be automated, and wouldn’t a configuration file be easier and safer to maintain? Yes. :)

In fact, Google Cloud Functions allows you to define a simple package.json file to declare and version your npm dependencies. As soon as Python is also supported, I expect that we’ll be able to simply deploy a pip requirements.txt file. Let’s see what happens.

Deployments and versioning

As I mentioned in the previous section, I don’t like the way AWS Lambda handles deployment packages and dependencies. It forces you to re-deploy a (potentially huge) deployment package every time you change your code or update a dependency.

On the other hand, I love the possibility of having multiple versions of the same Lambda function. This makes deploying and testing a new version very easy, even from the AWS Console UI. The real trick is binding versions to aliases so that you can easily switch to new versions (or roll back to older ones) with a couple of clicks. Linking stable versions of your functions to API Gateway stages such as dev, stage, prod, etc. requires a little bit of manual configuration, but it’s totally worth it. I recommend setting up an API Gateway stage variable and using it to invoke given AWS Lambda aliases.

On this front, Google Cloud Functions chose not re-invent the wheel and devised a developer-friendly solution that allows you to achieve versioning with git (i.e. a given branch or tag) even though you would need to host your repo on Cloud Source Repositories. I’m looking forward to a more general solution that includes other mainstream git solutions such as GitHub, BitBucket, etc.

Invocations, events, and logging

Both AWS Lambda and Google Cloud Functions support the event-driven approach. This means that you can trigger a function whenever something interesting happens within the cloud environment. They also support a simple HTTP approach.

AWS Lambda can be invoked by nearly every other AWS service including S3, SNS, SES, DynamoDB, Kinesis, Cognito, CloudWatch, etc.  You can configure API Gateway to invoke a given Lambda function and obtain a RESTful interface for free (almost), including authentication, caching, parameters mapping, etc.

Google Cloud Functions currently only supports internal events by Google Cloud Storage (i.e. Object Change Notifications) and through Google Cloud Pub/Subtopics (Google’s globally distributed message bus that automatically scales as you need it). HTTP invocations are already natively supported. You simply need to deploy your function with a trigger-http flag. Currently, you need to explicitly configure and deploy your Google Cloud Functions for each different trigger type.

As far as logging, both services are well integrated with their corresponding logging management services: Amazon CloudWatch and Google Cloud Logging. I personally find CloudWatch better integrated, better documented, and with charts that are easy to configure charts (kind-of).

Load testing and statistics

I took the time to perform some load tests on arbitrary JavaScript involving pure computation (i.e. generate 1,000 md5 hashes for each invocation). This gave me the opportunity to play with the two different dependency management systems because I needed to include the md5 npm module.

I configured a linearly incremental load of five minutes, up to about 70 requests per second. The two charts represent the average response time and the average number of requests per second.

Please note that these charts use the same scale for both dimensions. Also, I’ve deployed and tested both functions in the corresponding EU-west region (Ireland).

AWS Lambda Load Test

AWS Lambda Load Test

Google Cloud Functions Load Test

Google Cloud Functions Load Test

As you can see, there is a noticeable difference in the average response time: Google Cloud Functions consistently keeps it between 130 and 200ms, with a strange increase during the final minutes of my test (maybe due to the decreasing load?).

On the other hand, AWS Lambda’s response time is much higher and reveals an interesting rectangular pattern. The service seems to internally scale up after the load reached 20 req/s. When the load stabilizes around 30 req/s, it seems to scale down (i.e. response time raised to 600ms) and then it scales up again with a load of 40+ req/s.

Since each function invocation is returning a relatively heavy JSON response (almost 50KB), I assumed network performance had an impact on the resulting response time, independently of the actual computation. I quickly verified this assumption and modified both functions to return a simple “OK” message. I noticed a consistent improvement in the new AWS Lambda function, whose response time dropped between 200 and 300ms. The new Google Cloud Functions was not drastically affected, but its response time dropped to only 100ms.

Given these results, I would say that the computational difference is still relevant, but the network probably has a bigger impact if your application involves heavy HTTP responses. Apparently, as we already discussed back in 2014, Google’s networking just works better, and I would assume that the native HTTP integration is faster as well compared to Amazon API Gateway. Although Google’s networking is great, it still lacks critical features such as authentication and caching.

Function code compatibility between AWS and Google

Unfortunately for us, AWS Lambda and Google Cloud Functions are not directly compatible with each other. Google Cloud Functions is still in alpha and things can change, but I assume that Google won’t make the effort to be compatible with AWS Lambda without a compelling reason.

If you already have a few Lambda functions, in most cases, they are also interacting with at least another AWS service. Your Lambda functions are probably using some IAM roles and plenty of AWS details so you wouldn’t easily migrate to another cloud vendor anyway. However, in plenty of other cases your Lambda functions involve pure computation or simple input/ouput logic (i.e. read from a queue, write into a database, process an image, etc.). In these cases, you may be tempted to try your Lambda functions on Google Cloud Functions as well, even if just to evaluate the service, or reduce your costs. You can request your account to be whitelisted here.

What about an automated conversion tool?

Luckily, I took the time to develop a simple conversion tool that will definitely speed up the porting process of your JavaScript Lambda functions. It correctly handles the event/context functionality mapping and automatically comments incompatible attributes and methods. I really hate manual refactoring or porting tasks, so I hope it will be useful for some of you.

For example, a very simple function like the following:

would become something very similar to this:

As you can guess, the conversion is quite intuitive and shouldn’t take you more than a couple of minutes. However, things start to get much more complicated and time-consuming if you have a very complex function (especially if you defined additional utility functions that require both the original event and context objects).

Alpha testing conclusions

Google Cloud Functions looks very promising, and I am looking forward to the long list of upcoming features. I will continue to run tests and monitor trusted Cloud Functions tester groups, which already contains plenty of suggestions, improvements, and feedback. I personally hope to see many more tools that will enable cross-platform development in a serverless fashion.

If you enjoyed the article, feel free to comment and let us know what you think of the serverless revolution. We are happily using Lambda Functions in the Cloud Academy platform as well, and we can’t wait to see what will happen in the near future (and if you’re not familiar with it, take a look at the Serverless Framework).