Durable.Context (Durable v0.1.0-rc)

View Source

Context management for workflow execution.

The context is a key-value store that persists across steps within a workflow. It provides a way to share state between steps and is automatically persisted to the database after each step completes.

Usage

defmodule MyApp.OrderWorkflow do
  use Durable
  use Durable.Context

  workflow "process_order" do
    step :init do
      order = input().order
      put_context(:order_id, order.id)
      put_context(:items, order.items)
    end

    step :calculate_total do
      items = get_context(:items)
      total = Enum.sum(Enum.map(items, & &1.price))
      put_context(:total, total)
    end

    step :finalize do
      %{
        order_id: get_context(:order_id),
        total: get_context(:total)
      }
    end
  end
end

Context Storage

During execution, context is stored in the process dictionary for fast access. After each step completes, the context is persisted to the database. When a workflow resumes (e.g., after a sleep or wait), the context is restored from the database.

Summary

Functions

Injects context management functions into the calling module.

Appends a value to a list in the context.

Returns the entire context map.

Returns the current step name.

Deletes a key from the context.

Gets a value from the context by key.

Gets a value from the context by key with a default.

Checks if a key exists in the context.

Increments a numeric value in the context.

Returns the initial workflow input.

Deep merges a map into the context.

Checks if a parallel step succeeded.

Returns a specific parallel step's result by name.

Returns the full parallel results map from context.

Merges a map into the context.

Puts a single key-value pair into the context.

Updates a context value using a function.

Returns the current workflow ID.

Functions

__using__(opts)

(macro)

Injects context management functions into the calling module.

append_context(key, value)

@spec append_context(atom() | String.t(), any()) :: :ok

Appends a value to a list in the context.

If the key doesn't exist, creates a new list with the value.

Examples

append_context(:events, %{type: :clicked, timestamp: DateTime.utc_now()})

context()

@spec context() :: map()

Returns the entire context map.

Examples

ctx = context()
# => %{order_id: 123, items: [...]}

current_step()

@spec current_step() :: atom() | nil

Returns the current step name.

Examples

step = current_step()

delete_context(key)

@spec delete_context(atom() | String.t()) :: :ok

Deletes a key from the context.

Examples

delete_context(:temporary_data)
delete_context("temporary_data")

get_context(key)

@spec get_context(atom() | String.t()) :: any()

Gets a value from the context by key.

Returns nil if the key doesn't exist.

Examples

order_id = get_context(:order_id)

get_context(key, default)

@spec get_context(atom() | String.t(), any()) :: any()

Gets a value from the context by key with a default.

Examples

count = get_context(:retry_count, 0)
count = get_context("retry_count", 0)

has_context?(key)

@spec has_context?(atom() | String.t()) :: boolean()

Checks if a key exists in the context.

Examples

if has_context?(:order_id) do
  # ...
end

increment_context(key, amount \\ 1)

@spec increment_context(atom() | String.t(), number()) :: :ok

Increments a numeric value in the context.

If the key doesn't exist, starts from 0.

Examples

increment_context(:retry_count, 1)
increment_context(:processed_items, 1)

input()

@spec input() :: map()

Returns the initial workflow input.

Examples

order = input().order

merge_context(map)

@spec merge_context(map()) :: :ok

Deep merges a map into the context.

Examples

merge_context(%{settings: %{notifications: true}})

parallel_ok?(step_name)

@spec parallel_ok?(atom()) :: boolean()

Checks if a parallel step succeeded.

Examples

step :handle, fn ctx ->
  if parallel_ok?(:payment) do
    # payment succeeded
  else
    # payment failed
  end
end

parallel_result(step_name)

@spec parallel_result(atom()) :: {:ok, any()} | {:error, any()} | nil

Returns a specific parallel step's result by name.

Examples

step :handle, fn ctx ->
  case parallel_result(:payment) do
    {:ok, payment} -> # handle success
    {:error, reason} -> # handle error
  end
end

parallel_results()

@spec parallel_results() :: map()

Returns the full parallel results map from context.

The results map contains tagged tuples: %{step_name => {:ok, data} | {:error, reason}}

Examples

parallel do
  step :payment, fn ctx -> {:ok, %{id: 123}} end
  step :delivery, fn ctx -> {:error, :not_found} end
end

step :handle, fn ctx ->
  results = parallel_results()
  # => %{payment: {:ok, %{id: 123}}, delivery: {:error, :not_found}}
end

put_context(map)

@spec put_context(map()) :: :ok

Merges a map into the context.

Examples

put_context(%{order_id: 123, customer_id: 456})

put_context(key, value)

@spec put_context(atom() | String.t(), any()) :: :ok

Puts a single key-value pair into the context.

Examples

put_context(:order_id, 123)
put_context("order_id", 123)

update_context(key, fun)

@spec update_context(atom() | String.t(), (any() -> any())) :: :ok

Updates a context value using a function.

Examples

update_context(:retry_count, &(&1 + 1))
update_context(:items, &[new_item | &1])

workflow_id()

@spec workflow_id() :: String.t() | nil

Returns the current workflow ID.

Examples

id = workflow_id()