Serverless Functions using Rust and WebAssembly

Front Line (Web) Assembly

Background

In a previous post I gave an overview of how to create functions for Oracle Functions or Fn Project, when you want to use a language for which there is no FDK (Function Development Kit).

The approach I took was to create a "helper function" which provides a bare minimum interface between Fn and the function.

This is all well and good, but it is not a full FDK.  Some of the capabilities of the supported FDKs are absent.  For example, the helper is not aware of values that have been configured for the function or application via the context.

So in this post, I want to explore a different approach, which will
  • enable the capabilities of the provided FDKs
  • support functions in a language for which there is no FDK

WebAssembly

WebAssembly ("wasm") is an open standard W3C that defines a portable binary format.  By compiling code to WebAssembly we are able to run it in a browser.  This means that developers can
  • write code in a language other than JavaScript
  • compile it to WebAssembly
  • run it in the browser
WebAssembly is supported in the current versions of all major browsers.

Interestingly it is also possible to run WebAssembly in Node.js.

Polyglot Functions: Node and WebAssembly

So if we write our functions in a language that can be compiled to WebAssembly, then we should be able to run those functions in Node.js.  Since there is an FDK for Node.js, this means we can leverage the capabilities of the FDK to run our WebAssembly function.

Rust

Rust is a comparatively new language that has generated a lot of interest in the developer community since its launch.  It is statically typed, compiled and fast.  It has generated particular interest from teams who are concerned about memory safety, due to its innovative approach to memory management.

At the time of writing (June 2020) there isn't an FDK for Rust.  However Rust does work with WebAssembly.

Once you have compiled your Rust code to create a WebAssembly package, you can publish this with npm, and use it like a regular JavaScript package.

So we should be able to
  1. Write our function code in Rust
  2. Create a WebAssembly package
  3. Publish the package to npmjs
  4. Create a Node.js function in the usual way
  5. Reference the WebAssembly package in package.json
  6. Call the methods in the package from our JavaScript code in the usual way
To build WebAssembly packages from Rust, we're going to use a tool called wasm-pack.

Building the Function

Setup

To build this example, we need to do the following (if not already installed):

Install Rust

Once Rust is installed, install wasm-pack:
cargo install wasm-pack

Install Node.js

Install the Fn CLI to build functions and deploy them to the Oracle Functions service (preferred) or in a local test environment.  If you're using OCI Cloud Shell the Fn CLI is already installed

You'll need to configure your context for the Fn CLI.  Follow the appropriate instructions for:
Then create an app to deploy your functions to.

All the code in this sample is available on GitHub.

Create a WebAssembly package with wasm-pack

Run the following command:
$ wasm-pack new rust-fn

Add serialisation support

If we want to be able to serialise and deserialise objects between Rust and JS then we need to modify the Cargo.toml file so that the [dependencies] section matches the example:

Expose Rust Functions

Edit the /src/lib.rs file so that it matches the example:

The key here is the annotations

The main one to consider is:

#[wasm_bindgen]

Functions with this annotation will be able to be called from JavaScript.

The other important annotations are:

#[derive(Serialize)]
#[derive(Deserialize)]

These declare the associated type to be serialisable or deserialisable.

There are two styles of function in this example.  The simplest use strings to pass data to and from JavaScript, and the others (with the suffix _json) use JSON and serialization

Compile the Package

Run
wasm-pack build --target nodejs --scope <your-node-js-user-id>
This will create a package that can be used from node, and published to npm.

From the pkg directory, run the following to publish your package:
npm publish --access=public

Creating functions

You can now run fn init in the usual way to create a function:
fn init --runtime node <function-name>
Edit the package.json to include your package (mine is @crush-157/rust-fn)

Then you can reference the package in JavaScript and call it's functions in the usual way.

Sample Functions

There are two sample functions in this example:

lazy-rust

The JSON payload is parsed in JavaScript

busy-rust

The JSON payload is parsed by the Rust function

It's up to you whether you'd rather parse JSON in JS and pass it to the function (possibly easier but not terribly elegant), or pass the JSON to Rust and parse it there (maybe a bit more work but cleaner).

So now we can use the Node FDK to run functions written in any language that compiles to WebAssembly.

Comments

Popular posts from this blog

Wot no FDK?

The Case of the Vanishing Dockerfile

Honey I Shrunk The Container