The Alibaba Cloud Function Compute Programming Model

Image for post
Image for post

By Wang Hongqi

I am going to introduce the Alibaba Cloud Function Compute programming model. I originally posted it on my twitter and feel it might be good to put the thread in one article. Let’s see how it differs from other vendors and what that brings to the user.

First you will see the familiar (event) handler, e.g. Python, as you’ve seen in other FaaS programming models.

import json

def handler(event, context):
evt = json.loads(event)
result = 'hello %s' % evt.get('name', 'world')
return result

Actually it’s a bit different, the event is not a dict. It’s a bytearray/ByteBuffer⋯so you may need to unmarshal it by yourself based on your needs.

  • Cons: you can’t assume it’s JSON.
  • Pros: the platform doesn’t force you to convert your non-JSON thing to JSON format.

e.g. You can pass protobuf format event or an image file to the function, the platform just passes the event to the handler without any examination/transformation. The protobuf and type checking idea mentioned by @timallenwagner can be partially satisfied.

By contrast, if the event is JSON type, to pass a binary value, it needs to be converted to something like base64 encoded and formed into a JSON string, passed to the service which converts it to dict, and then passed to the handler which converts it to the binary.

The return value is not restricted to JSON either for the same reason.

Next you will also see the familiar HTTP (req/resp) interface in the programming model. Why? Because using JSON to describe request is tedious/inefficient and not native to the web development.

If you look at tools around Lambda, you see many frameworks/platforms trying to give the HTTP interface back to the developer, e.g. @vercel now, @tjholowaychuk up. It’s doable but not quite nice. The shim approach also needs to deal with the event and request/response conversion, which is an overhead.

The Function Compute (FC) service avoids that by exposing the HTTP function interface, e.g. Python WSGI, NodeJS express, Java servlet etc. It not only avoids the unnecessary transformation overhead, but also brings the native experience back. Running a Django or Flask app on FC requires just few lines of code.

Is this enough? Customers also ask for more runtime support. The custom runtime approach seems the way to go. But what interface the custom runtime should expose? Like the event function model?

Pure functions may look cool, but writing a webserver based application isn’t a headache for many languages. By contrast, converting a webserver based application to pure functions requires a lot of work, not to mention the overhead between the event and HTTP request.

So why not just let users run the server and app as functions? To develop a custom runtime, instead of talking to some APIs like the Lambda approach, You just write your app listening to port 9000.

What does this bring to users?

  1. It further eases the migration of web applications (e.g. alibabacloud.com/help/doc-detai⋯).
  2. Anyone can use this simple approach to write functions. Maybe it’s not a good name cause the function and runtime are bundled together.

One more thing, you can have an optional initializer handler. It’s a good place to do some preparation work, e.g. initializing HTTP/DB client. This has been asked by @theburningmonk, @timallenwagner and more. It not only makes the code a bit cleaner, but also reduces the cold-start time. I’ve been seeing vendors working hard to reduce the platform cold-start, e.g. how to get the function instance up and running quickly.

But what about business cold-start? e.g. loading a model which takes few seconds or longer.

import time

time.sleep(5)

def handler(event, context):
time.sleep(2)
return ''

If I invoke this function twice concurrently, there will be 2 cold-starts.

The platform can’t do anything because it thinks the instance is ready to serve request while it’s actually not ready. What’s the difference with an initializer? The platform executes it before serving the request.

import time

def initializer(context):
time.sleep(5)

def handler(event, context):
time.sleep(2)
return ''

If I invoke the function twice concurrently, there will be 1 cold-start. Why? because the 1st function instance becomes available earlier and hence serves the 2nd request while the 2nd instance is still being initialized.

This is just one example that shows how the initializer can be used to reduce cold-start latency. There are more optimizations that can be done in the background, e.g. updating functions also triggers instance update. A bit more info can be found here.

This concludes the current state of Function Compute programming model. Will there be more styles? Maybe, let us know what you need.

Original Source:

Written by

Follow me to keep abreast with the latest technology news, industry insights, and developer trends.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store