# `TCC`
[🔗](https://github.com/vcnovaes/toccata/blob/v0.1.0/lib/tcc.ex#L1)

TCC (Try-Confirm-Cancel) protocol implementation for distributed transactions.

TCC is a two-phase commit protocol for distributed transactions that provides
eventual consistency across multiple services. It consists of three phases:

- **Try**: Reserve resources and validate business rules
- **Confirm**: Commit the transaction and apply the changes
- **Cancel**: Rollback the transaction and release reserved resources

## Example

    defmodule PaymentService do
      def try_payment(effects, params) do
        # Reserve funds
        case reserve_funds(params.account_id, params.amount) do
          {:ok, reservation_id} ->
            {:ok, effects, Map.put(params, :reservation_id, reservation_id)}
          {:error, reason} ->
            {:error, reason}
        end
      end

      def confirm_payment(effects, params) do
        # Deduct funds
        case deduct_funds(params.reservation_id) do
          {:ok, _} -> {:ok, effects, params}
          {:error, reason} -> {:error, reason}
        end
      end

      def cancel_payment(effects, params) do
        # Release reserved funds
        case release_funds(params.reservation_id) do
          {:ok, _} -> {:ok, effects, params}
          {:error, reason} -> {:error, reason}
        end
      end
    end

    TCC.new()
    |> TCC.run(:payment, &PaymentService.try_payment/2,
                         &PaymentService.confirm_payment/2,
                         &PaymentService.cancel_payment/2)
    |> TCC.run(:inventory, &InventoryService.try_reserve/2,
                           &InventoryService.confirm_reserve/2,
                           &InventoryService.cancel_reserve/2)
    |> TCC.execute(%{account_id: "123", amount: 100})

# `effects`
[🔗](https://github.com/vcnovaes/toccata/blob/v0.1.0/lib/tcc.ex#L57)

```elixir
@type effects() :: map()
```

# `params`
[🔗](https://github.com/vcnovaes/toccata/blob/v0.1.0/lib/tcc.ex#L58)

```elixir
@type params() :: any()
```

# `stage_name`
[🔗](https://github.com/vcnovaes/toccata/blob/v0.1.0/lib/tcc.ex#L59)

```elixir
@type stage_name() :: atom()
```

# `t`
[🔗](https://github.com/vcnovaes/toccata/blob/v0.1.0/lib/tcc.ex#L56)

```elixir
@type t() :: TCC.Transaction.t()
```

# `actions`
[🔗](https://github.com/vcnovaes/toccata/blob/v0.1.0/lib/tcc.ex#L334)

```elixir
@spec actions(t()) :: [TCC.Action.t()]
```

Returns the list of actions in the transaction.

# `async_execute`
[🔗](https://github.com/vcnovaes/toccata/blob/v0.1.0/lib/tcc.ex#L224)

```elixir
@spec async_execute(t(), params()) :: Task.t()
```

Executes the TCC transaction asynchronously.

Returns a task that can be awaited later.

## Examples

    task = TCC.async_execute(transaction, %{amount: 100})
    # Do other work...
    result = Task.await(task, :infinity)

# `execute`
[🔗](https://github.com/vcnovaes/toccata/blob/v0.1.0/lib/tcc.ex#L207)

```elixir
@spec execute(t(), params()) ::
  {:ok, effects(), params()} | {:error, stage_name(), any(), effects()}
```

Executes the TCC transaction with the given parameters.

The execution follows these steps:
1. Execute all Try functions in sequence
2. If all Try functions succeed, execute all Confirm functions
3. If any Try function fails, execute Cancel functions for completed Try operations
4. Return the final result with effects and status

## Parameters

  * `transaction` - The TCC transaction struct
  * `params` - Initial parameters to pass to the first action

## Returns

  * `{:ok, effects, result}` - Transaction completed successfully
  * `{:error, failed_stage, reason, effects}` - Transaction failed with error details

## Examples

    case TCC.execute(transaction, %{amount: 100, account_id: "123"}) do
      {:ok, effects, result} ->
        IO.puts("Transaction succeeded")
      {:error, stage, reason, effects} ->
        IO.puts("Transaction failed at stage #{stage}: #{inspect(reason)}")
    end

# `finally`
[🔗](https://github.com/vcnovaes/toccata/blob/v0.1.0/lib/tcc.ex#L309)

```elixir
@spec finally(t(), function() | {module(), atom(), list()}) :: t()
```

Appends a function that will be triggered after TCC execution completes.

The final hook receives `:ok` if the transaction succeeded or `:error` if it failed,
along with the options passed to `execute/2`.

This is useful for job queue acknowledgment or cleanup tasks that should run
regardless of transaction outcome.

## Example

    def acknowledge_job(:ok, _opts), do: IO.puts("Job completed successfully")
    def acknowledge_job(:error, _opts), do: IO.puts("Job failed")

    TCC.new()
    |> TCC.finally(&acknowledge_job/2)
    |> TCC.run(:payment, &try_fn/2, &confirm_fn/2, &cancel_fn/2)
    |> TCC.execute(%{})

Final hooks can also be MFA tuples:

    TCC.new()
    |> TCC.finally({MyModule, :acknowledge, [:extra_arg]})
    |> TCC.execute(%{})

# `new`
[🔗](https://github.com/vcnovaes/toccata/blob/v0.1.0/lib/tcc.ex#L83)

```elixir
@spec new(keyword()) :: t()
```

Creates a new TCC transaction.

## Options

  * `:timeout` - Maximum time in milliseconds for the entire transaction (default: 30_000)
  * `:retry_limit` - Number of retries for confirm/cancel phases (default: 3)
  * `:async` - Whether to execute confirm/cancel phases asynchronously (default: false)
  * `:telemetry_prefix` - Prefix for telemetry events (default: [:tcc])

## Examples

    iex> transaction = TCC.new()
    iex> is_struct(transaction, TCC.Transaction)
    true

    iex> transaction = TCC.new(timeout: 60_000, retry_limit: 5)
    iex> transaction.opts.timeout
    60_000

# `options`
[🔗](https://github.com/vcnovaes/toccata/blob/v0.1.0/lib/tcc.ex#L340)

```elixir
@spec options(t()) :: map()
```

Returns the transaction options.

# `run`
[🔗](https://github.com/vcnovaes/toccata/blob/v0.1.0/lib/tcc.ex#L112)

```elixir
@spec run(t(), stage_name(), function(), function(), function()) :: t()
```

Adds a TCC action to the transaction pipeline.

Each action must implement three functions:
- Try function: `(effects, params) -> {:ok, effects, params} | {:error, reason}`
- Confirm function: `(effects, params) -> {:ok, effects, params} | {:error, reason}`
- Cancel function: `(effects, params) -> {:ok, effects, params} | {:error, reason}`

## Parameters

  * `transaction` - The TCC transaction struct
  * `name` - Unique name for this action
  * `try_fun` - Function to execute in the Try phase
  * `confirm_fun` - Function to execute in the Confirm phase
  * `cancel_fun` - Function to execute in the Cancel phase

## Examples

    TCC.new()
    |> TCC.run(:payment, &PaymentService.try_payment/2,
                         &PaymentService.confirm_payment/2,
                         &PaymentService.cancel_payment/2)

# `run_async`
[🔗](https://github.com/vcnovaes/toccata/blob/v0.1.0/lib/tcc.ex#L170)

```elixir
@spec run_async(t(), stage_name(), function(), function(), function(), keyword()) ::
  t()
```

Adds an asynchronous TCC action to the transaction pipeline.

Async actions are executed in parallel during the Try phase. They are awaited
before the next synchronous action or at the end of the Try phase before
proceeding to the Confirm phase.

If there is an error in an async action, TCC will await for other async
actions to complete and then execute the Cancel phase for all completed actions.

## Options

  * `:timeout` - Time in milliseconds to wait for the async action (default: 5000)
  * `:retry_limit` - Retry limit for confirm/cancel phases

## Example

    TCC.new()
    |> TCC.run(:payment, &try_payment/2, &confirm_payment/2, &cancel_payment/2)
    |> TCC.run_async(:inventory, &try_inventory/2, &confirm_inventory/2, &cancel_inventory/2)
    |> TCC.run_async(:shipping, &try_shipping/2, &confirm_shipping/2, &cancel_shipping/2)
    |> TCC.run(:notification, &try_notify/2, &confirm_notify/2, &cancel_notify/2)
    |> TCC.execute(%{})

In this example, `:inventory` and `:shipping` are executed in parallel after
`:payment` completes. They are awaited before `:notification` starts.

# `run_with_opts`
[🔗](https://github.com/vcnovaes/toccata/blob/v0.1.0/lib/tcc.ex#L137)

```elixir
@spec run_with_opts(t(), stage_name(), function(), function(), function(), keyword()) ::
  t()
```

Adds a TCC action with custom options.

## Options

  * `:timeout` - Timeout for this specific action (overrides global timeout)
  * `:retry_limit` - Retry limit for this specific action
  * `:async` - Whether this action should be executed asynchronously

## Examples

    TCC.new()
    |> TCC.run_with_opts(:payment,
         &PaymentService.try_payment/2,
         &PaymentService.confirm_payment/2,
         &PaymentService.cancel_payment/2,
         timeout: 10_000, retry_limit: 5)

# `with_cancel_error_handler`
[🔗](https://github.com/vcnovaes/toccata/blob/v0.1.0/lib/tcc.ex#L278)

```elixir
@spec with_cancel_error_handler(t(), module()) :: t()
```

Registers an error handler for cancel phase failures.

The handler module must implement the `TCC.CancelErrorHandler` behaviour.
By default, critical errors during cancel phase are raised.

## Example

    TCC.new()
    |> TCC.with_cancel_error_handler(MyCancelErrorHandler)
    |> TCC.run(:payment, &try_fn/2, &confirm_fn/2, &cancel_fn/2)
    |> TCC.execute(%{})

# `with_tracer`
[🔗](https://github.com/vcnovaes/toccata/blob/v0.1.0/lib/tcc.ex#L253)

```elixir
@spec with_tracer(t(), module()) :: t()
```

Registers a tracer module for the transaction.

The tracer module must implement the `TCC.Tracer` behaviour.
Tracers are called before and after each phase of TCC execution.

## Example

    defmodule MyTracer do
      @behaviour TCC.Tracer

      @impl true
      def handle_event(name, action, state) do
        IO.puts("Action #{name}: #{action}")
        state
      end
    end

    TCC.new()
    |> TCC.with_tracer(MyTracer)
    |> TCC.run(:payment, &try_fn/2, &confirm_fn/2, &cancel_fn/2)
    |> TCC.execute(%{})

---

*Consult [api-reference.md](api-reference.md) for complete listing*
