# Temporal Workflow Orchestration **Server:** `temporal.temporal.svc.cluster.local:7233` (cluster-internal) **Web UI:** `kubectl port-forward -n temporal svc/temporal-web 8088:8088` **Namespace:** `temporal` ## When to Use - **Long-running operations** — Tasks that take minutes/hours (send email, process batch, retry with backoff) - **State machines** — Multi-step workflows with decision logic - **Retries & timeouts** — Built-in exponential backoff, automatic retry - **Audit trail** — Full history of workflow executions (why it happened, when, by whom) ## Quick Start **1. Access Temporal Web UI:** ```bash k port-forward -n temporal svc/temporal-web 8088:8088 # http://localhost:8088 ``` **2. Define workflow (Go example):** ```go package workflows import ( "time" "go.temporal.io/sdk/workflow" "go.temporal.io/sdk/activity" ) type Inputs struct { OrderID string Amount float64 } // Workflow definition func OrderProcessing(ctx workflow.Context, input Inputs) (string, error) { // Step 1: Charge payment chargeResult := "" err := workflow.ExecuteActivity( ctx, ChargePayment, input.OrderID, input.Amount, ).Get(ctx, &chargeResult) if err != nil { return "", err } // Step 2: Send confirmation email (retry 3x on failure) emailResult := "" opts := workflow.ActivityOptions{ StartToCloseTimeout: time.Minute, RetryPolicy: &temporal.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 2, MaximumAttempts: 3, }, } ctx = workflow.WithActivityOptions(ctx, opts) err = workflow.ExecuteActivity(ctx, SendConfirmationEmail, input.OrderID).Get(ctx, &emailResult) if err != nil { return "", err } return "order_processed", nil } // Activity: payment processing func ChargePayment(ctx context.Context, orderID string, amount float64) (string, error) { // Call payment gateway return "payment_successful", nil } // Activity: email notification func SendConfirmationEmail(ctx context.Context, orderID string) (string, error) { // Send email return "email_sent", nil } ``` **3. Register & start workflow:** ```go import "go.temporal.io/sdk/client" client, _ := client.Dial(client.Options{ HostPort: "temporal.temporal.svc.cluster.local:7233", }) // Start workflow execution run, _ := client.ExecuteWorkflow(ctx, client.StartWorkflowOptions{ ID: "order-123", TaskQueue: "orders", }, OrderProcessing, Inputs{OrderID: "123", Amount: 99.99}) // Wait for result var result string run.Get(ctx, &result) ``` ## Configuration | Key | Value | |-----|-------| | Server | `temporal.temporal.svc.cluster.local:7233` | | Web UI | `localhost:8088` (via port-forward) | | Database | PostgreSQL (managed by helmfile) | | Task queue | `default`, `orders`, `emails` (custom per app) | | Retention | 30 days (configurable) | ## Common Patterns **Retry with exponential backoff:** ```go opts := workflow.ActivityOptions{ StartToCloseTimeout: 5 * time.Minute, RetryPolicy: &temporal.RetryPolicy{ InitialInterval: time.Second, BackoffCoefficient: 2.0, // double wait time each retry MaximumInterval: time.Minute, // cap at 1 min between retries MaximumAttempts: 5, // give up after 5 tries }, } ctx = workflow.WithActivityOptions(ctx, opts) ``` **Wait for signal (user approval):** ```go // Workflow waits for approval signal approval := "" workflow.GetSignalChannel(ctx, "approval").Receive(ctx, &approval) if approval == "approved" { // Continue workflow } else { return "", errors.New("request denied") } ``` **Parallel activities:** ```go // Execute email & SMS in parallel emailFuture := workflow.ExecuteActivity(ctx, SendEmail, userID) smsFuture := workflow.ExecuteActivity(ctx, SendSMS, userID) // Wait for both to complete emailFuture.Get(ctx, nil) smsFuture.Get(ctx, nil) ``` **Scheduled workflow (cron):** ```go run, _ := client.ExecuteWorkflow(ctx, client.StartWorkflowOptions{ ID: "daily-report", CronSchedule: "0 9 * * MON-FRI", // 9 AM weekdays WorkflowTaskTimeout: time.Hour, }, GenerateDailyReport, nil) ``` ## Monitoring **Web UI:** - List workflows: http://localhost:8088/namespaces/default/workflows - View execution history: Click workflow ID - See activity logs, errors, retry attempts **Grafana dashboard:** `svc-temporal` (auto-configured) **Key metrics:** - `temporal_workflow_execution_duration_seconds` — workflow time - `temporal_activity_execution_duration_seconds` — activity time - `temporal_activity_execution_failed_total` — failed activities ## Integration with Story Crater **Example: Process message via Temporal:** ```go // In message handler client, _ := temporal.Dial(/* ... */) run, _ := client.ExecuteWorkflow(ctx, client.StartWorkflowOptions{ ID: fmt.Sprintf("msg-%s", messageID), TaskQueue: "story-crater", }, ProcessMessageWorkflow, Message{ ID: messageID, Body: body, Source: "kafka-queue", }) // Non-blocking: workflow runs independently // Check status later ``` ## Troubleshooting **Workflow stuck:** ```bash # Check Temporal server health k get pods -n temporal # View workflow execution history (via Web UI or CLI) tctl workflow show --workflow-id order-123 # Terminate stuck workflow tctl workflow terminate --workflow-id order-123 ``` **Activity retrying endlessly:** ```go // Add max attempts or timeout RetryPolicy: &temporal.RetryPolicy{ MaximumAttempts: 5, // must have this! } ``` **PostgreSQL connection fails:** ```bash # Check temporal pod logs k logs -n temporal pod/temporal-0 # Verify database is running k get pods -n ddb ``` See `/TROUBLESHOOTING.md` for full incident guide.