DoubleEntryLedger.Occ.Helper (double_entry_ledger v0.4.0)
View SourceProvides helper functions for managing Optimistic Concurrency Control (OCC) in the Double Entry Ledger system.
This module contains utilities for implementing OCC retry logic, including backoff timing, error tracking, and retry state management. It offers a consistent framework for handling concurrent modification conflicts across the application.
Key Functionality
- Retry Management: Calculate and apply appropriate backoff delays
- Error Tracking: Update error maps with retry information and messages
- Configuration: Access OCC-related configuration settings like max retries
- Timing Utilities: Calculate next retry times for command scheduling
Configuration
The module's behavior can be configured through the following application environment variables:
:max_retries- Maximum number of retry attempts (default: 5):retry_interval- Base interval for retries in milliseconds (default: 200):next_retry_after_interval- Time to wait before next retry attempt (default: max_retries * retry_interval)
Usage Examples
Setting a delay based on current attempt number:
# Pause execution with exponential backoff
DoubleEntryLedger.Occ.Helper.set_delay_timer(attempt_number)Updating an error map after an OCC conflict:
updated_error_map = DoubleEntryLedger.Occ.Helper.update_error_map(
existing_error_map,
current_attempt_number,
%{step_1: result_1, step_2: result_2}
)Calculating timestamps for retry scheduling:
{now, next_retry_time} = DoubleEntryLedger.Occ.Helper.get_now_and_next_retry_after()Implementation Notes
This module uses an exponential backoff strategy where retry intervals double with each successive attempt, helping to reduce contention over time while maintaining responsiveness for quick resolutions.
Summary
Functions
Calculates the delay duration based on the number of attempts using exponential backoff.
Can be configured via the :retry_interval application environment.
The delay doubles with each successive retry.
Returns the maximum number of retries allowed.
Can be configured via the :max_retries application environment.
Generates an appropriate error message for OCC conflicts.
Creates a changeset to mark a command as timed out due to OCC conflicts.
Returns the retry interval in milliseconds.
Can be configured via the :retry_interval application environment.
Pauses execution for a calculated delay based on the number of attempts.
Updates the given ErrorMap with a new error message, incrementing the retry count
and updating the steps completed so far.
Functions
Calculates the delay duration based on the number of attempts using exponential backoff.
Can be configured via the :retry_interval application environment.
The delay doubles with each successive retry.
Parameters
attempts: The current attempt number (counts down from max_retries).
Examples
iex> DoubleEntryLedger.Occ.Helper.delay(4)
20
iex> DoubleEntryLedger.Occ.Helper.delay(2)
80
@spec max_retries() :: integer()
Returns the maximum number of retries allowed.
Can be configured via the :max_retries application environment.
Examples
iex> DoubleEntryLedger.Occ.Helper.max_retries()
5
Generates an appropriate error message for OCC conflicts.
Creates different messages depending on the number of attempts remaining:
- When attempts > 1, shows a retry message with delay time and attempts left
- When attempts <= 1, shows a final timeout message
Parameters
attempts(integer()): The current attempt number
Returns
String.t(): A formatted error message
Examples
iex> DoubleEntryLedger.Occ.Helper.occ_error_message(3)
"OCC conflict detected, retrying after 40 ms... 2 attempts left"
iex> DoubleEntryLedger.Occ.Helper.occ_error_message(1)
"OCC conflict: Max number of 5 retries reached"
@spec occ_timeout_changeset( DoubleEntryLedger.Command.t(), DoubleEntryLedger.Command.ErrorMap.t() ) :: Ecto.Changeset.t()
Creates a changeset to mark a command as timed out due to OCC conflicts.
This function prepares a changeset that updates a command to the :occ_timeout status,
recording the error information from the error map and updating the retry count.
Parameters
command(Command.t()): The command to update%{errors: errors, retries: retries}: An error map containing:errors: List of error messages accumulated during retry attemptsretries: Number of retry attempts made
Returns
Ecto.Changeset.t(): A changeset ready to update the command with timeout information
@spec retry_interval() :: integer()
Returns the retry interval in milliseconds.
Can be configured via the :retry_interval application environment.
Examples
iex> DoubleEntryLedger.Occ.Helper.retry_interval()
10
@spec set_delay_timer(integer()) :: :ok
Pauses execution for a calculated delay based on the number of attempts.
Parameters
attempts: The current attempt number.
Examples
iex> DoubleEntryLedger.Occ.Helper.set_delay_timer(2)
:ok
@spec update_error_map(DoubleEntryLedger.Command.ErrorMap.t(), integer(), map()) :: DoubleEntryLedger.Command.ErrorMap.t()
Updates the given ErrorMap with a new error message, incrementing the retry count
and updating the steps completed so far.
Parameters
error_map(ErrorMap.t()): The current error map to be updated.attempts(integer()): The number of attempts made so far.steps_so_far(map()): A map representing the steps completed so far.
Returns
ErrorMap.t(): The updated error map with the new error message, incremented retry count, and updated steps.
Examples
iex> error_map = %DoubleEntryLedger.Command.ErrorMap{errors: [], retries: 0, steps_so_far: %{}}
iex> updated_error_map = DoubleEntryLedger.Occ.Helper.update_error_map(error_map, 3, %{step: "processing"})
iex> updated_error_map.retries
1
iex> updated_error_map.steps_so_far
%{step: "processing"}
iex> length(updated_error_map.errors) > 0
true