Simple Long Running Workflows with Ballerina

Introduction

When you think about workflows, you will generally think about BPEL/BPMN as the dominant prevailing technologies. But these technologies come with their own complexities and a learning curve. This approach may not be that natural to the typical developer.

What if you can just use your friendly general purpose programming language to tackle the intricacies of long running processes as well?. This is what Ballerina is trying to do. Ballerina being a programming language which has first class support for integration concepts, and having built-in support for asynchronous programming, concurrency, and transactions, it makes it inherently capable of modelling long running processes as well.

Use Case: Loan Processing Workflow

Let’s go through a simple long running workflow of a long approval service. A graphical representation of this as a flow diagram can be seen in Figure 01.

Figure 01: Loan Approval Service

In the above process, when a loan application is submitted to the system, there are two outcomes. That is, either it is approved immediately by checking some automated criteria, or else, it goes into a manual review state. In the manual review state, the underwriter manually checks the application for the details, takes the decision, and notifies the system of the decision; which is either the loan being approved or denied. This status is then marked in the system for that application, and later on, interested parties can query this information and see the status of the application.

If we model this process as a typical application, we will need some special behavior in order to block the execution of the application, at the manual review phase, and later on, continue from this point, when we get a review message to the application from outside. This need to happen efficiently, in order for us to not to block any critical resources such as I/O or computing threads blocked in these points. Due to Ballerina’s non-blocking I/O infrastructure, this is handled automatically.

Checkpointing

Ballerina introduced several new features with version 0.982.0, where your program can be checkpointed, and the state at this checkpoint can be persisted. We can signal the Ballerina runtime, that they should be looking to activate checkpoints, by annotating the required service resource entry points with the annotation “@interruptible”. Listing 01 shows how this is done in our loan processing service.

Listing 01: Loan Processing Service marked as “interruptible”

So, now the question is, at which point does Ballerina checkpoint the running application?. Checkpointing can be done explicitly using the API call runtime:checkpoint(). Also, we can specifically annotate connector actions to signal that the runtime should checkpoint when the action is called. At the moment, The HTTP related actions are automatically checkpointed in this manner, and you can make your own actions do the same as needed. Also, Ballerina channel send/receive actions get checkpointed automatically. So in this way, if your execution crashes in the middle of a function/resource, the system will restore from the last checkpoint, when the program is re-run.

Going with the Flow

Let’s take a closer look at our implementation of the workflow. The entry point for the workflow is from the “start_application” resource. Here, the application details are given in the form of the “ApplicationInfo” record, where the incoming JSON message is mapped to that. From here on, the full process is handled by the “processApplication” function, as seen in Listing 02.

Listing 02: Process Application

This function drives the full workflow, and the intermediate ask-for-more-information points are implemented using Ballerina channels. These channels are used to associate a specific process instance and a point in execution with a given correlation id. A channel receive basically stops the execution of the current program, until the channel is sent a message with the same correlation id (key), and at this point, the original application instance will come into existence again, and start the execution from where it left off. This behavior can be seen implemented in the function “processReviewApplication” and the resource “add_application_review” as shown in Listing 03 and Listing 04 respectively.

Listing 03: Process Review Application
Listing 04: Add Application Review

Workflow User Roles/Scopes

In the stage of submitting a loan application review, this action must be a privileged operation, that cannot be done by all the users in the system. So we have to make sure, proper user authentication and authorization should happen. This can be accomplished by the authentication framework provided by Ballerina, where an “AuthProvider” can be associated with a service, and its resources. This configuration can be seen below in Listing 05.

Listing 05: Authentication Provider Configuration

Here, we simply configure a file based user store, and the authentication is done using basic auth.

Now, in our “add_application_review” resource, as seen in Listing 04, this is used to add the application review by doing the API call. For this, we’ve made the configuration, so the user must have the “underwriter” scope in accessing the resource. In our sample users, we have given this scope to the user “tom”, where the user “jack” doesn’t have any scopes defined. So in the runtime, only tom should be able to add application reviews. For more information on secure programming with Ballerina, check out the BBG here.

Demo Run

The full source code and the associated resources to run the sample can be found here.

  • Start the service with the following command:-
# ballerina run — config users.toml loan_service.bal
  • Service calls and responses:-
Listing 06: Sample run commands

Future Work

Ballerina workflow support is still under development. There will be many more features incorporated into language in the upcoming releases, to make the workflow authoring as intuitive as possible.