Suspending

Kontinue allows Executions to sleep or suspend by creation Suspensions. Suspensions are Kubernetes resources that pause an Execution until a condition is met. These are typically created when an Execution calls kontinue.Sleep() or kontinue.Suspend().

Suspensions are a key part of kontinue’s durability model. Because the suspension state is stored in Kubernetes, the Execution can be resumed by any worker after failures or restarts (and not restart the sleep from the beginning).

Sleep

Use kontinue.Sleep() to pause an Execution for a specified duration:

func DeployWorkflow(ktx *kontinue.ExecutionContext, args *DeployArgs) error {
    // Deploy to staging
    if err := deployToStaging(args); err != nil {
        return err
    }

    // Wait 1 hour before deploying to production
    if err := kontinue.Sleep(ktx, 1 * time.Hour); err != nil {
        return err
    }

    // Deploy to production
    return deployToProduction(args)
}

Unlike time.Sleep(), kontinue.Sleep() is replay-safe:

  • The sleep duration is stored in a Suspension resource with a resumeAt timestamp
  • If the worker crashes during the sleep, a new worker will resume from where it left off
  • The Execution won’t re-sleep the full duration on resume

Sleeps can also be resumed early via the CLI or UI (see Resuming below).

Manual Suspend

Use kontinue.Suspend() to pause an Execution indefinitely until manually resumed:

func ApprovalWorkflow(ktx *kontinue.ExecutionContext, args *ApprovalArgs) error {
    // Prepare the change
    if err := prepareChange(args); err != nil {
        return err
    }

    // Wait for manual approval
    if err := kontinue.Suspend(ktx, &kontinue.SuspendOptions{}); err != nil {
        return err
    }

    // Apply the approved change
    return applyChange(args)
}

This is useful for:

  • Human approval workflows
  • Waiting for external events
  • Pausing for manual verification steps
  • Integration with external ticketing systems

Suspension Resource

When Sleep() or Suspend() is called, kontinue creates a Suspension resource:

apiVersion: kontinue.cloud/v1alpha1
kind: Suspension
metadata:
  name: my-execution-sleep-abc123
  ownerReferences:
    - apiVersion: kontinue.cloud/v1alpha1
      kind: Execution
      name: my-execution
spec:
  resumeAt: "2025-01-15T15:30:00Z"  # Only for Sleep, nil for Suspend
status:
  phase: Suspended
  startedAt: "2025-01-15T14:30:00Z"

Suspension Phases

PhaseDescription
SuspendedWaiting for resumeAt time or manual resume
CompletedSuspension resolved, Execution can continue
CanceledSuspension was canceled

The parent Execution tracks its suspended state in status.suspendedOn, which references the Suspension resources it’s waiting on.

Resuming Suspensions

Using the CLI

Resume a specific suspension:

kontinue resume <suspension-name>

Resume all direct suspensions for an execution:

kontinue resume <execution-name>

Resume recursively (including child executions):

kontinue resume <execution-name> -r

Using the UI

In the kontinue UI:

  1. Navigate to the Execution details
  2. View the suspended state and pending Suspensions
  3. Click “Resume” to complete the Suspension

Using the API

// Resume a specific suspension
err := client.ResumeSuspension(ctx, suspension)

// Resume all suspensions for an execution
err := client.ResumeExecution(ctx, execution, &client.ResumeOptions{
    Recursive: true,  // Include child executions
})

When a Suspension is resumed:

  1. The Suspension’s phase changes to Completed
  2. The Execution detects the completion and continues
  3. The suspendedOn reference is removed from the Execution status

Named Suspensions

For determinism, you can specify a custom step name:

// Using SleepNamed with explicit name
err := kontinue.SleepNamed(ktx, &kontinue.SleepOptions{
    Duration: 1 * time.Hour,
    StepOptions: kontinue.StepOptions{
        Name: "wait-for-cooldown",
    },
})

// Using Suspend with explicit name
err := kontinue.Suspend(ktx, &kontinue.SuspendOptions{
    StepOptions: kontinue.StepOptions{
        Name: "approval-gate",
    },
})

This ensures the Suspension name is consistent across replays, even if execution order changes.