An Overview of LFaaS and “Logic Injection” — the New Cloud Pattern for Modeling Business Logic

A practical approach that brings low-code, serverless, secured 3rd-party code sandbox and rapid deployment together on Kubernetes

Alan Wang
FST Network
12 min readApr 28, 2022

--

Photo by Samson on Unsplash

Author’s note: the technological details described in this article is based on an older version of FST Network’s LOC. Since LOC is still activity evolving to meet our customer’s needs and to stay competitive, please refer to our official blog and documentation for latest updates.

Key Takeaways

  • LFaaS (Logic Functions as a Service) allows users to model modern, complex business logic (data process) with a low-code environment.
  • “Logic Injection” provides a way to safely execute 3rd-party code in a sandbox which that can deployed rapidly without cold starts.
  • Kubernetes’ Custom Resource and Deno runtime play a key role in LFaaS and Logic Injection respectively.
  • LFaaS and Logic Injection are the core software architecture of FST Network’s Logic Operating Centre (LOC), a new data governance solution for enterprises.

Related reading:

Introduction — What is FaaS and LFaaS?

Function-as-a-Service, or FaaS, is a kind of cloud computing service that allows developers to build, compute, run, and manage application packages as functions without having to maintain their own infrastructure.

FaaS is an event-driven execution model that runs in stateless containers and those functions manage server-side logic and state through the use of services from a FaaS provider.

— Red Hat: What is Function-as-a-Service (FaaS)?

Photo by Redd on Unsplash

FaaS (Function as a Service) is a cloud service model that is often associated with the term serverless: a user can run a code snippet (in the form of a function) practically without having to setup anything. The function would mostly be run as a container and would be triggered by an event (event-driven architecture, EDA).

Amazon announced the first commercial cloud FaaS, AWS Lambda, in 2014, followed by Google Cloud Functions and Azure Functions in 2016, and more after that. Numerous open-source FaaS frameworks also appeared on Github like OpenFaas and Knative. Many of them offers a wide range of programming languages and tools.

However, popular as FaaS has been, it has some limitations:

  • Single function is too simple to model complex business logic. This is probably one of the reason that why FaaS is getting overtaken by the rise of low-code SaaS (Software as a Service) or PaaS (Platform as a Service).
  • Difficult to ensure code safety on commercial cloud platforms.
  • Commercial cloud platforms has time limit for functions therefore there are cold start delays.
  • It’s not easy to deploy and maintain your own FaaS platform either.
Photo by Max Duzij on Unsplash

Although FST Network’s LFaaS (Logic Functions as a Service) — which is used in the data governance solution, Logic Operating Centre (LOC) —was not meant to directly address FaaS’s shortcomings, it is indeed designed for modeling complex business logic while retaining the ease-to-use of FaaS platforms. Later in this article, we will see that some of the FaaS issues can be improved by another pattern called “Logic Injection”.

To understand what’s the difference between LFaas and FaaS, we’ll start by looking at the LOC data process.

LOC data process vs. FaaS function

Truth to be told, there is actually only one major difference between a LOC data process and a FaaS function: a data process can have one or more functions whereas a FaaS function is limited to one.

  • Each function in a LOC data process is called a (generic) logic which represents a step in the business logic.
  • The last function in a data process is called a aggregator which is responsible for putting together a final output from other logic’s results.
  • The data process can be triggered by events, message queues (MQ) or schedulers. The trigger can also contain multiple data sources so clients can have logic functions do or not do something in different scenarios.
  • Logic and aggregator functions cannot directly talk to each other or anyone outside for safety reasons. Instead, they have to use so-called agents (runtime driver libraries) provided by LOC to exchange data, either via LOC session store/event store or external APIs/databases (data sources). We will take a closer look at this later in this article.

Aggregators do not have access to LOC event store because they are not meant to. Aggregators should be used for preparing the final data to be returned. We will demonstrate the logic code and its result in another article.

Data processes creation from the users’ point of view

From the point of view of LOC users, LFaaS is similar to FaaS — write code as functions and deploy them. LOC take cares of the deployment detail (package it as a container) and manages it in the Kubernetes cluster.

Data process from the point of view of users. Data processes are deployed by their dev team and connects actors in the business logic.

Once the data process is up and running, it will listen to LOC’s API gateway (a LOC internal service) for any triggers (which may come from users, systems or LOC scheduler) and return desired data to whoever needs them. LOC provides a non-intrusive way to quickly implement data logic between data users and data storages.

Sounds pretty simple, isn’t it? Well, there is actually way more going on beneath the surface. It is also something often asked by dev teams from FST Network’s clients.

How LOC data process works under the hood — LFaaS + Logic Injection

LFaaS and Logic Injection are not merely concepts. They are already implemented in LOC and have been adopted by our clients. Here’s the overview of how these patterns are designed to work inside LOC.

Kubernetes’ Custom Resource and Operator pattern

In our Kubernetes’ introduction article, we’ve briefly mentioned that Kubernetes allows users to add Custom Resources (CR) as containers. It’s a great way to extend the Kubernetes API for more flexibility. In fact, CRs are already widely adopted in Kubernetes as well. It’s often associated with Operator pattern.

So what exactly is Operator pattern?

The Operator pattern aims to capture the key aim of a human operator who is managing a service or set of services…

Kubernetes’ operator pattern concept lets you extend the cluster’s behaviour without modifying the code of Kubernetes itself by linking controllers to one or more custom resources. Operators are clients of the Kubernetes API that act as controllers for a Custom Resource.

— Kubernetes document: Operator pattern

Sounds difficult to understand? In fact, an Operator (which resembles a human operator) in Kubernetes is basically the combination of following two components:

  • Custom Resource Definition (CRD) — manifest of your custom container image (where is the image and how many containers should be running).
  • Controller or control loop — custom code that tells Kubernetes how to understand and manage your custom container (since the CR is not the standard Kubernetes containers).

We currently use CRI-O as the container runtime.

In the Kubernetes article we’ve mentioned that you can “declare” a manifest and Kubernetes will create, deploy and monitor these containers as your wish.

This applies to CRDs as well: the deployed custom containers (also called Custom Objects) works just like Kubernetes “pods”, except that we can implement additional behaviors on them, which is exactly we need for running complex business logic.

CRD auto-generation

When a user “deploys” a data process in LOC, the information of it will be handed to LOC’s built-in service Orchestrator, which is running in the Kubernetes cluster. The Orchestrator will auto-generate the corresponding CRD manifest and controller code .

Unlike most people use Golang, this magical, full-auto process in LOC as well as the generated controller are both written in Rust. The CRD and controller would then be upload into the Kubernetes cluster.

Photo by Utsav Srestha on Unsplash

Once the CRD is in place, Kubernetes will (also automatically) pull a pre-compiled data process image (all data processes use the same image) from a container registry — for example, Amazon ECR or a local Harbor Registry — and create/deploy container(s) as instructed.

Since Amazon ECR is not free, FST Network usually would help our clients to deploy their own Harbor services.

This is more of less what FaaS does: to provide a service that auto-deploy functions without user intervention. The differences is that data processes are far more capable than standard FaaS functions.

API route configuration

The Orchestrator also plays another role: to updates LOC’s API Gateway service, which also runs in the Kubernetes cluster. The API route configuration is a separate file which defines how to trigger the data process (for example, the API path). API routes enables a data process can be invoked and read correctly, hence it’s an important part of LFaaS as well.

We don’t have to create CRDs or custom containers from API route configuration. API Gateway apps (there are more than one) will be rolling-updated so the service won’t go down at any time.

The API Gateway is implemented with Nginx and can accept message sources like Kafka, Pulsar, etc.

Here’s a very short example of what you might see using the kubectl tool (there’s more in reality but we only list a few API routes and data processes here):

The problem of FaaS function cold start

Photo by Eric Ward on Unsplash

LFaaS is only half of the reason that why LOC data processes are more powerful than traditional FaaS functions. We’ve mentioned that on commercial cloud platforms there are cold start delays:

In the background, FaaS uses containers to encapsulate and execute the functions. When an user invokes a function, FaaS keeps the container running for a certain time period after the execution of the function (warm) and if another request comes in before the shutdown, the request is served instantaneously. Cold start is about the time it takes to bring up a new container instance when there are no warm containers available for the request.

On the Serverless cold start problem

Cold starts can last a few even a dozen seconds, which introduces huge impacts to complex business logic. However, it seems not possible to run client’s third party code without using custom containers tailored for each scenario. Automatically build custom images is not fast either.

The solution? We can, in fact, create an extra runtime inside the universal container, which will load and run the code after the container is deployed. This is what we called Logic Injection.

Meet Deno, a powerful JS runtime

Photo by Hannah Pemberton on Unsplash

Deno is a JavaScript/TypeScript runtime created by Ryan Dahl, the creator of Node.js, to address several weakness in Node.js:

  • Deno itself is a compact, self-contained and standalone executable binary written in Rust.
  • Offers a secure sandbox with V8 JavaScript engine that restricts file system/network access.
  • Has built-in package manager (no need to install NPM, Node Package Manager) and TypeScript compiler.
  • Supports JavaScript ES6 and many new Web APIs.

A problem with NPM is that it’s vulnerable to sabotages. Although these are rare and usually be fixed very quickly, the story of left-pad (2016) and peacenotwar (2022) still demonstrated how fragile the Node.js ecosystem can be.

In other words, Deno runtime is safe, small and very portable. Whether or not Deno can replace Node.js is still open to debate — Node.js is still extremely popular in both front-end and back-end communities — but in our case, Deno is exactly the thing we were looking for.

A Deno binary file is included in LOC’s data process container image. The actual user logic code would be first uploaded to a cloud storage (like Amazon S3, depending on the platform), and once the container is deployed, they would then be downloaded and executed in the Deno runtime.

Logic code are injected into containers at runtime to transform them into different data processes. No cold starts or custom image builds delay.

“Logic injection” vs. “sidecar injection” pattern

You may have heard of a similar-named cloud pattern called sidecar injection:

This pattern is named Sidecar because it resembles a sidecar attached to a motorcycle. In the pattern, the sidecar is attached to a parent application and provides supporting features for the application. The sidecar also shares the same lifecycle as the parent application, being created and retired alongside the parent. The sidecar pattern is sometimes referred to as the sidekick pattern and is a decomposition pattern.

— Microsoft Azure, Sidecar pattern

Photo by Jon Tyson on Unsplash

Sidecar is a container — it would be attached to the parent container as a proxy to add or modify behaviors of the latter, especially when you cannot or shouldn’t change the parent app.

Automatic sidecar injection has been supported in tools like Istio. Although this is a pretty cool pattern, it is not suitable for LOC: we do not know when a new container should be needed and do not wish to create two containers every time a user deploys a data process.

Since LOC data process are identical by nature, changing the internal code (inject the code logic instead of a whole container) is simpler and makes data process containers easier to manage.

Deno runtime setup and LOC agents

Photo by Fahmi Fakhrudin on Unsplash

There are other things injected into the LOC data process container image as well. For example, the LOC agents would initialize themselves so they can be used in the container.

The Deno runtime and initial LOC code together are also called a “snapshot”.

Also: LOC agents in the Deno runtime are only part of it. The main functionality of agents are in LOC core, which are implemented with Rust.

LOC data processes are designed so that you cannot access anything outside without using LOC agents: you are not allowed to install or upload non-LOC packages. This is a simple but effective way to ensure all third party logic code are guaranteed to be safe since they cannot be otherwise.

LOC agents are already built-in so you don’t have to import them. The functionalities they offer includes but not limit to:

  • HTTP requests (GET/POST/PATCH/PUT/DELETE)
  • Database (SQL Server, PostgreSQL and MySQL)
  • Remote file (SMB, FTP)
  • LOC event store, session store, logging
  • Return result from aggregator logic

Future prospects of supported languages

Right now, the “official” language for developing logic code are JavaScript and TypeScript, since that’s the two languages supported in Deno runtime. What about other languages? Can LOC support many languages like Golang, Java, Python and C# like the way AWS Lambda does?

In fact, Deno supports WebAssembly or WASM — which is a library that can execute WASM binaries in JavaScript VMs. Practually all major programming languages can be compiled into WASM format, and this is already adopted by almost all browsers as well as Node.js and Deno.

Sadly, we won’t discuss too much about WASM today. But yes: the LOC dev team does have plan to support WASM in the future, which would sure greatly expand the supported languages in LOC. So stay tuned to our blog for any exciting new updates!

Pattern Recap

  • LFaaS = LOC data process (multiple logic functions) + CRD auto-generation + Kubernetes Operator pattern.
  • Logic Injection = Deno runtime + external code on cloud storage + dynamic code loading after containers are deployed.
  • LFaaS + Logic Injection = low code development + secured 3rd-party code sandbox + rapid container deploy in Kubernetes.

Relate Reading

This article was written with great help from FST Network’s dev team.

For more info about Logic Operating Centre (LOC) or demonstration request, please contact support@fstk.io.

--

--