Introduction

In this blogpost I will demonstrate how temporal can be used to implement Workers based execution model accross different services.

Temporal provides durable execution. It maintains the state of functions, so that we can keep track of the function execution.

Functions can be registered as Workflow if they are more deterministic, or else if they have non-deterministic functionalities like http calls, they can be registered as Activity

In this example we will create a Workflow in Java which will be picked by a Python worker.


Installation

Easiest way to install Temproal is via docker-compose

Follow the below steps:

  • Clone the repo: https://github.com/temporalio/docker-compose.git

  • cd temporal/docker-compose

  • docker compose up -d The default docker-compose.yml file uses postgres, you can use other docker compose files for using different databases that are supported eq. mysql

This will start the Temporal Cluster and some other dependencies like database and elastic-search. The cluster will listen on port 7233
It will also start the UI which can be accessed on port 8080.

Implementation

This implementation will require the following components

  • Python Worker which will execute the Activity
  • Java Workflow which will call for the execution of the python activity
  • Workflow Starter for the Java Workeflow and another Worker in Java which will execute the workflow code.


Writing a Java Workflow

Include the temporal dependency. If you are using maven, add below dependency to your pom.xml

<dependency>
    <groupId>io.temporal</groupId>
    <artifactId>temporal-sdk</artifactId>
    <version>1.19.1</version>
</dependency>


Create a Workflow Interface SimpleWorkflow.java

@WorkflowInterface
public interface SimpleWorkflow {

    @WorkflowMethod
    String simpleActivity(String value);
}


Implement the WorkflowInterface in SimpleWorkflowImpl.java

First create an AcitvityStub.
The temporal Worker will communicate with the cluster using a task-queue and both the worker and workflow should be resgistered on the same task-queue.


ActivityOptions options =
    ActivityOptions.newBuilder()
        .setTaskQueue("simple-task-queue")
        .build();

private final ActivityStub externalActivity = Workflow.newUntypedActivityStub(options);

Since we will create our Worker in Python, we register the Activity using newUntypedActivityStub

Next, lets implement the WorkflowMethod and call for the execution of python activity

@Override
public String SimpleActivity(String name) {

    String result = 
        externalActivity.execute("PythonActivity", String.class, name);
    
    return result;        
}

We will name our activity in python code as PythonActivity


Writing the Workerflow Starter and Java Worker code

We will write workerflow starter and Java worker code in the same file WorkflowStarter.java

package simpleWorkflow

import io.temporal.client.WorkflowClient;
import io.temporal.client.WorkflowOptions;
import io.temporal.client.WorkflowStub;
import io.temporal.serviceclient.WorkflowServiceStubs;
import io.temporal.worker.Worker;
import io.temporal.worker.WorkerFactory;

public class WorkflowStarter {

    public static final String TASK_QUEUE = "java-task-queue";

    public static void main(String[] args) throws Exception {

        WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();

        WorkflowClient client = WorkflowClient.newInstance(service);
        
        WorkflowOptions options = WorkflowOptions.newBuilder()
            .setWorkflowId("simple-workflow")
            .setTaskQueue(TASK_QUEUE)
            .build();


        WorkerFactory factory = WorkerFactory.newInstance(client);

        // create a new worker
        Worker worker = factory.newWorker(TASK_QUEUE);

        // register the java workflow
        worker.registerWorkflowImplementationTypes(SimpleWorkflowImpl.class);

        // start the worker
        factory.start();

        System.out.println("Started the Worker, starting workflow...");

        SimpleWorkflow workflow = client.newWorkflowStub(SimpleWorkflow.class, options);

        // execute the workflow
        String result = workflow.pythonActivity(args[0]);

        String workflowId = WorkflowStub.fromTyped(workflow).getExecution().getWorkflowId();

        factory.shutdown();

    }
}

In the above code,

  • we registered the Workerflow on java-task-queue with locally running Temporal instance.
  • Started a Java worker listening on java-task-queue
  • Started the Workflow. The Java worker should pick the task, execute the workflow which will in turn call the python activity.


Writing the Python Worker

Finally we need the Python worker to execute the python activity.
We will define the activity and write the worker in the same file python_worker.py

@activity.defn(name="PythonActivity")
async def simple_activity(name: str) -> str:
    print(f"Hello {name}")
    return "Success"


async def main():
    temporal_client = await client.Client.connect("localhost:7233")

    python_worker =  worker.Worker(
        client=temporal_client,
        task_queue="simple-task-queue",
        activities=[simple_activity],
    )
    await python_worker.run()

if __name__ == "__main__":
    asyncio.run(main())

In the above code,

  • we registered the worker on the locally running Temporal instance and listening on queue simple-task--queue
  • We defined the activity and registered the acitvity with the worker.



Execution

In the final step we will execute the two programs,

  • WorkflowStarter.java
  • python_worker.py

In different terminals execute the programs.

First start the python worker

python python_worker.py

Next execute the java program

mvn compile exec:java -Dexec.mainClass="simpleWorkflow.WorkflowStarter" -Dexec.ar
gs="Sohit"

The execution can be monitored on localhost:8080



Conclusion

Temporal helps execute the code using workers easily. We can have multiple workers listening on the same taskQueue and temporal will take care of the distribution of the tasks.

Complete code can be found at: https://github.com/Sohit1212/temporal-polyglot