Bedrock.Internal.TransactionBuilder.Tx (bedrock v0.5.2)

View Source

Opaque transaction type for building and committing database operations.

This module provides an immutable transaction structure that accumulates reads, writes, and range operations. Transactions can be committed to produce the final mutation list and conflict ranges for resolution.

Summary

Functions

Add a single key read conflict to the transaction.

Add a read conflict range to the transaction.

Add a single key write conflict to the transaction.

Add a write conflict range to the transaction.

Merge storage range read results into the transaction state for conflict tracking.

Enhanced version of merge_storage_range_with_writes that handles pending writes correctly based on shard boundaries and has_more flag.

Merge a storage read result into the transaction state for conflict tracking.

Get the repeatable read value for a key within the transaction.

Types

fetch_fn()

@type fetch_fn() :: (key(), term() -> {{:ok, value()} | {:error, term()}, term()})

key()

@type key() :: binary()

mutation()

@type mutation() ::
  {:set, key(), value()}
  | {:clear, key()}
  | {:clear_range, start :: binary(), end_ex :: binary()}
  | {:atomic, atom(), key(), binary()}

range()

@type range() :: {start :: binary(), end_ex :: binary()}

t()

@type t() :: %Bedrock.Internal.TransactionBuilder.Tx{
  mutations: [mutation()],
  range_reads: [range()],
  range_writes: [range()],
  reads: %{required(key()) => value() | :clear},
  writes:
    :gb_trees.tree(
      key(),
      value()
      | :clear
      | {:add, binary()}
      | {:min, binary()}
      | {:max, binary()}
      | {:bit_and, binary()}
      | {:bit_or, binary()}
      | {:bit_xor, binary()}
      | {:byte_min, binary()}
      | {:byte_max, binary()}
      | {:append_if_fits, binary()}
      | {:compare_and_clear, binary()}
    )
}

value()

@type value() :: binary()

Functions

add_or_merge(list, s, e)

@spec add_or_merge([range()], start :: key(), end_ex :: key()) :: [range()]

add_read_conflict_key(t, key)

@spec add_read_conflict_key(t(), key()) :: t()

Add a single key read conflict to the transaction.

This is a convenience function that adds a read conflict for a single key by converting it to a single-key range (key to Key.key_after(key)).

Parameters

  • t - The transaction
  • key - The key to add as a read conflict

Examples

iex> tx = Tx.new()
iex> tx = Tx.add_read_conflict_key(tx, "my_key")
iex> tx.range_reads
[{"my_key", "my_key\0"}]

add_read_conflict_range(t, start_key, end_key)

@spec add_read_conflict_range(t(), key(), key()) :: t()

Add a read conflict range to the transaction.

This function adds the specified range to the transaction's read conflict tracking. Overlapping and adjacent ranges are automatically merged for efficiency.

Parameters

  • t - The transaction
  • start_key - The inclusive start key of the range
  • end_key - The exclusive end key of the range

Examples

iex> tx = Tx.new()
iex> tx = Tx.add_read_conflict_range(tx, "a", "z")
iex> tx.range_reads
[{"a", "z"}]

iex> tx = Tx.new()
iex> tx = Tx.add_read_conflict_range(tx, "a", "m")
iex> tx = Tx.add_read_conflict_range(tx, "k", "z")
iex> tx.range_reads
[{"a", "z"}]

add_write_conflict_key(t, key)

@spec add_write_conflict_key(t(), key()) :: t()

Add a single key write conflict to the transaction.

This is a convenience function that adds a write conflict for a single key by converting it to a single-key range (key to Key.key_after(key)).

Parameters

  • t - The transaction
  • key - The key to add as a write conflict

Examples

iex> tx = Tx.new()
iex> tx = Tx.add_write_conflict_key(tx, "my_key")
iex> tx.range_writes
[{"my_key", "my_key\0"}]

add_write_conflict_key_unless(t, key, bool)

@spec add_write_conflict_key_unless(t(), key(), no_write_conflict :: boolean()) :: t()

add_write_conflict_range(t, start_key, end_key)

@spec add_write_conflict_range(t(), key(), key()) :: t()

Add a write conflict range to the transaction.

This function adds the specified range to the transaction's write conflict tracking. Overlapping and adjacent ranges are automatically merged for efficiency.

Parameters

  • t - The transaction
  • start_key - The inclusive start key of the range
  • end_key - The exclusive end key of the range

Examples

iex> tx = Tx.new()
iex> tx = Tx.add_write_conflict_range(tx, "a", "z")
iex> tx.range_writes
[{"a", "z"}]

iex> tx = Tx.new()
iex> tx = Tx.add_write_conflict_range(tx, "a", "m")
iex> tx = Tx.add_write_conflict_range(tx, "k", "z")
iex> tx.range_writes
[{"a", "z"}]

add_write_conflict_range_unless(t, min_key, max_key_ex, bool)

@spec add_write_conflict_range_unless(
  t(),
  key(),
  key(),
  no_write_conflict :: boolean()
) :: t()

atomic_operation(t, k, operation, value)

@spec atomic_operation(t(), key(), atom(), binary()) :: t()

clear(t, k, opts \\ [])

@spec clear(t(), key(), opts :: keyword()) :: t()

clear_range(t, s, e, opts \\ [])

@spec clear_range(t(), start :: key(), end_ex :: key(), opts :: keyword()) :: t()

commit(t, read_version)

get(t, k, fetch_fn, state)

@spec get(t(), key(), fetch_fn(), state) ::
  {t(), {:ok, value()} | {:error, :not_found}, state}
when state: term()

merge_storage_range_read(t, resolved_start_key, resolved_end_key, key_values)

@spec merge_storage_range_read(t(), key(), key(), [{key(), value()}]) :: t()

Merge storage range read results into the transaction state for conflict tracking.

This function is used after KeySelector range resolution to merge resolved keys and values into the transaction's read state, and add the range to range_reads.

merge_storage_range_with_writes(tx, storage_results, has_more, query_range, shard_range)

@spec merge_storage_range_with_writes(
  t(),
  [{key(), value()}],
  has_more :: boolean(),
  query_range :: {key(), key()},
  shard_range :: Bedrock.key_range()
) :: {t(), [{key(), value()}]}

Enhanced version of merge_storage_range_with_writes that handles pending writes correctly based on shard boundaries and has_more flag.

When has_more = false, this indicates the storage server has given us all data in its authoritative range, so we should include pending writes beyond the storage results up to the boundary of the query range and shard range.

merge_storage_read(t, key, value)

@spec merge_storage_read(t(), key(), value() | :not_found) :: t()

Merge a storage read result into the transaction state for conflict tracking.

This function is used after KeySelector resolution to merge the resolved key and value into the transaction's read state, ensuring proper conflict detection.

new()

@spec new() :: t()

repeatable_read(t, key)

@spec repeatable_read(t(), key()) :: value() | :clear | nil

Get the repeatable read value for a key within the transaction.

Checks both writes and reads, returning the value if the key has been accessed in this transaction, or nil if the key is unknown to the transaction. This ensures repeatable read semantics - the same key returns the same value throughout the transaction.

set(t, k, v, opts \\ [])

@spec set(t(), key(), value(), opts :: keyword()) :: t()