Skip to main content
Infinitic is an event-driven orchestration framework that coordinates distributed workflows and services. This page explains how all components work together to provide durable, scalable, and reliable workflow execution.

System Overview

Infinitic consists of four main components:

Clients

Application code that starts and interacts with workflows

Workers

Runtime components that execute workflows and services

Apache Pulsar

Event streaming platform for reliable message delivery

Storage

State persistence for workflows (Redis, Postgres, MySQL)

Core Components

1. Clients

Clients are application code that interacts with Infinitic:
  • Start workflows with parameters and tags
  • Send signals to running workflows via channels
  • Query status of workflows and tasks
  • Manage workflows (cancel, retry, complete timers)
// Client creates workflow stub and calls methods
val client = InfiniticClient.fromYamlResource("/config.yml")
val workflow = client.newWorkflow(OrderWorkflow::class.java)
val result = workflow.processOrder(orderId)
Source: infinitic-client/src/main/kotlin/io/infinitic/clients/InfiniticClient.kt

2. Workers

Workers are long-running processes that execute workflows and services. Each worker can run multiple components:

Service Executor

Executes service methods when called by workflows:
class PaymentServiceImpl : PaymentService {
    override fun charge(orderId: String, amount: Double): PaymentResult {
        // Execute actual payment logic
        return paymentGateway.process(orderId, amount)
    }
}

Workflow Executor

Executes workflow tasks (the workflow code itself):
class OrderWorkflowImpl : Workflow(), OrderWorkflow {
    private val paymentService = newService(PaymentService::class.java)
    
    override fun processOrder(orderId: String): OrderResult {
        // Workflow orchestration logic
        val payment = paymentService.charge(orderId, 99.99)
        return OrderResult(payment.transactionId)
    }
}

Workflow State Engine

Manages workflow state, handles events, and dispatches tasks:
  • Stores workflow state in configured storage (Redis/Postgres/MySQL)
  • Processes events (task completed, task failed, timer completed)
  • Dispatches new workflow tasks
  • Manages workflow lifecycle

Tag Engines

Manage tags for workflows and services:
  • Map tags to workflow/service IDs
  • Enable lookup by custom identifiers
  • Support multiple IDs per tag
Source: infinitic-worker/src/main/kotlin/io/infinitic/workers/InfiniticWorker.kt

3. Apache Pulsar

Pulsar provides reliable, ordered message delivery:
  • Topics per entity: Separate topics for each service/workflow
  • Guaranteed delivery: Messages are persisted and replayed if needed
  • Ordered processing: Messages with the same key are processed in order
  • Scalability: Distribute load across consumers
Topic Structure:
infinitic/dev/service-executor/PaymentService
infinitic/dev/service-tag-engine/PaymentService
infinitic/dev/workflow-executor/OrderWorkflow
infinitic/dev/workflow-state-engine/OrderWorkflow
infinitic/dev/workflow-state-cmd/OrderWorkflow
infinitic/dev/workflow-tag-engine/OrderWorkflow

4. Storage Layer

State persistence for workflows: Key-Value Storage:
  • Workflow state by workflow ID
  • Tag mappings (tag → workflow IDs)
  • Serialized using efficient binary format
Supported Backends:
  • Redis: Fast, in-memory storage
  • PostgreSQL: Relational database with ACID guarantees
  • MySQL: Popular relational database
  • In-Memory: For testing only (not persistent)
Source: infinitic-storage/src/main/kotlin/io/infinitic/storage/

Message Flow

Starting a Workflow

Service Call from Workflow

Workflow State Management

Workflows maintain state across service calls:

State Components

  1. Workflow Properties: Current values of workflow fields
  2. Method State: Running methods and their local variables
  3. Step History: Completed steps and their results
  4. Running Commands: In-flight service calls and child workflows
  5. Timers: Active timers and their completion times

State Persistence

From infinitic-workflow-engine:
// Workflow state is stored as binary
interface WorkflowStateStorage {
    suspend fun getState(workflowId: WorkflowId): WorkflowState?
    suspend fun updateState(workflowId: WorkflowId, state: WorkflowState)
    suspend fun deleteState(workflowId: WorkflowId)
}
State is:
  • Serialized efficiently using binary format
  • Compressed optionally to reduce storage size
  • Cached optionally using Caffeine cache
  • Logged for debugging via LoggedWorkflowStateStorage
Source: infinitic-workflow-engine/src/main/kotlin/io/infinitic/workflows/engine/storage/

Execution Guarantees

Durability

Workflows survive failures through:
  1. State Persistence: Workflow state saved after each step
  2. Message Durability: Pulsar persists messages until acknowledged
  3. Replay from State: Workers replay workflow from saved state
  4. Idempotent Operations: Duplicate messages handled safely

Ordering

From InfiniticWorker.kt, messages are processed:
  • With key: Messages with same key processed sequentially
  • Without key: Messages processed concurrently up to concurrency limit
This ensures:
  • Workflow state updates are sequential
  • Service tasks can be parallel
  • Tag updates are consistent

At-Least-Once Delivery

All operations have at-least-once semantics:
  • Messages may be delivered multiple times
  • Services should be idempotent when possible
  • Workflow replay is deterministic (same inputs → same outputs)

Scalability

Horizontal Scaling

Workers:
  • Add more worker instances
  • Each consumes from same topics
  • Load distributed automatically by Pulsar
# Scale to 3 workers
kubectl scale deployment infinitic-worker --replicas=3
Pulsar:
  • Add more brokers for message throughput
  • Add more bookies for storage throughput

Vertical Scaling

Concurrency:
workflows:
  - name: OrderWorkflow
    concurrency: 50              # Parallel workflow tasks
    stateEngine:
      concurrency: 20            # State engine concurrency
Batching:
services:
  - name: EmailService
    batch:
      maxMessages: 100           # Batch up to 100 messages
      maxSeconds: 1              # Or every 1 second

Modules Overview

Infinitic codebase is organized into modules:
ModulePurposeLocation
infinitic-commonShared data structures and interfacesCore types, messages
infinitic-clientClient API and implementationInfiniticClient
infinitic-workerWorker runtime and configurationInfiniticWorker
infinitic-storageStorage abstraction and implementationsRedis, Postgres, MySQL
infinitic-cacheCaching layer for storageCaffeine cache
infinitic-transportTransport abstractionMessage routing
infinitic-transport-pulsarPulsar implementationPulsar topics, producers
infinitic-workflow-engineWorkflow state managementState engine, handlers
infinitic-task-executorService executionTask execution logic
infinitic-task-tagService tag managementTag engine
infinitic-workflow-tagWorkflow tag managementTag engine

Deployment Patterns

Monolithic Deployment

Single worker running all components:
# All-in-one worker
services:
  - name: PaymentService
    class: com.example.PaymentServiceImpl
    concurrency: 10
    tagEngine:
      concurrency: 5

workflows:
  - name: OrderWorkflow
    class: com.example.OrderWorkflowImpl
    concurrency: 5
    stateEngine:
      concurrency: 10
    tagEngine:
      concurrency: 5

Separated Deployment

Different workers for different responsibilities:
# Worker 1: Service executors only
services:
  - name: PaymentService
    class: com.example.PaymentServiceImpl
    concurrency: 50
# Worker 2: Workflow executors only
workflows:
  - name: OrderWorkflow
    class: com.example.OrderWorkflowImpl
    concurrency: 20
# Worker 3: Engines only
services:
  - name: PaymentService
    tagEngine:
      concurrency: 10

workflows:
  - name: OrderWorkflow
    stateEngine:
      concurrency: 20
    tagEngine:
      concurrency: 10

Kubernetes Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: infinitic-worker
spec:
  replicas: 3
  selector:
    matchLabels:
      app: infinitic-worker
  template:
    metadata:
      labels:
        app: infinitic-worker
    spec:
      containers:
      - name: worker
        image: myorg/infinitic-worker:latest
        env:
        - name: INFINITIC_CONFIG
          value: /config/infinitic.yml
        volumeMounts:
        - name: config
          mountPath: /config
      volumes:
      - name: config
        configMap:
          name: infinitic-config

Observability

Cloud Events

Infinitic emits Cloud Events for all operations:
class MyEventListener : CloudEventListener {
    override fun onEvent(event: CloudEvent) {
        when (event.type) {
            "task.started" -> metricsClient.increment("task.started")
            "task.completed" -> {
                metricsClient.increment("task.completed")
                val duration = event.extensionNames.get("duration")
                metricsClient.recordDuration("task.duration", duration)
            }
            "workflow.completed" -> {
                alertingClient.sendSuccess(event.source, event.id)
            }
        }
    }
}

Logging

From InfiniticWorker.kt:144-176, workers log in-flight message counts on shutdown:
In Flight Messages:
* TaskExecutor: 5 remaining (1000 received)
* WorkflowStateEngine: 2 remaining (500 received)

Metrics

Integrate with monitoring systems:
  • Message throughput
  • Task execution time
  • Workflow duration
  • Error rates
  • Queue depths

Best Practices

Separate Concerns

Run executors and engines in different workers for better scaling

Use Caching

Enable state caching to reduce storage load

Monitor Queues

Track Pulsar queue depths to detect bottlenecks

Plan for Failure

Design workflows to be resilient to service failures

Next Steps

Workflows

Learn about workflow patterns and features

Services

Implement services for your business logic

Workers

Deploy and configure workers

Clients

Interact with workflows from your applications

Build docs developers (and LLMs) love