Skip to main content

Quickstart

View Markdown

Prerequisites: JDK 25, the lertha CLI, and a running orchestrator. For a laptop, the fastest orchestrator is the throwaway one the CLI ships:

lertha dev

That gives you an orchestrator on localhost:7233 and a UI on localhost:8233. In production the address is the Lertha orchestrator's; see Configuration.

1. Define a task

data class GreetingInput(val greeting: String, val name: String)
data class Greeting(val text: String)

object GreetingTask : TaskHandler<GreetingInput, Greeting> {
override val type = taskType<GreetingInput, Greeting>("ComposeGreeting")

override suspend fun handle(input: GreetingInput, ctx: TaskContext): Greeting =
Greeting("${input.greeting}, ${input.name}!")
}

No annotations, no engine imports. The type name is what the UI and the CLI show.

2. Run a worker

fun main() {
EngineConnection.fromEnvironment().use { connection ->
connection.runner("lab").use { runner ->
runner.register(GreetingTask)
runner.start()
Thread.currentThread().join()
}
}
}

3. Start the task from application code

fun main(args: Array<String>) = runBlocking {
EngineConnection.fromEnvironment().use { connection ->
val orchestrator = connection.orchestrator()
val options = RunOptions(
id = "greeting-${UUID.randomUUID()}",
queue = "lab",
attemptTimeout = 10.seconds,
retry = RetryPolicy(maxAttempts = 5, initialInterval = 500.milliseconds),
)
val greeting = orchestrator.run(GreetingTask.type, GreetingInput("Hello", "World"), options)
println(greeting.text)
}
}

run is start followed by result(). Use start when the caller should not wait.

4. Reach the same worker from the CLI

Because the handler registers under its plain type name:

lertha task run --type ComposeGreeting --id cli-1 --queue lab --timeout 10s \
--input '{"greeting":"Hello","name":"CLI"}'

Next

  • Make it fail on purpose and watch the retries in the UI: Tasks.
  • Put a loop and a human around it: Flows.
  • The lab repository lerta-orchestration-lab has all of this wired, with a --fail N flag.