Electric.Postgres.Xid (electric v1.4.11)

View Source

Summary

Functions

Check if a transaction is in the future from the POV of the snapshot. In other words, if its xid is >= xmax, its changes are definitely not visible and won't become visible in this snapshot.

In Postgres, any 32-bit xid has ~2 billion values preceding it and ~2 billion values following it. Regular autovacuuming maintains this invariant. When we see a difference between two xids that is larger than 2^31, we know there's been at least one transaction ID wraparound. Given the invariant mentioned earlier, we assume there's been only one wraparound and so the xid whose value is larger precedes the other one (or, equivalently, the smaller xid belongs to a more recent transaction).

Compare two snapshots. Returns :lt if snapshot1 < snapshot2, :eq if equal, :gt if snapshot1 > snapshot2.

Guard function to check if xid_l == xid_r using the same wraparound logic as compare/2.

Guard function to check if xid_l < xid_r using the same wraparound logic as compare/2.

Types

anyxid()

@type anyxid() :: pos_integer()

cmp_result()

@type cmp_result() :: :lt | :eq | :gt

pg_snapshot()

@type pg_snapshot() :: {anyxid(), anyxid(), [anyxid()]}

Functions

after_snapshot?(xid, arg2)

@spec after_snapshot?(anyxid(), pg_snapshot()) :: boolean()

Check if a transaction is in the future from the POV of the snapshot. In other words, if its xid is >= xmax, its changes are definitely not visible and won't become visible in this snapshot.

compare(xid8_l, xid8_r)

@spec compare(anyxid(), anyxid()) :: cmp_result()

In Postgres, any 32-bit xid has ~2 billion values preceding it and ~2 billion values following it. Regular autovacuuming maintains this invariant. When we see a difference between two xids that is larger than 2^31, we know there's been at least one transaction ID wraparound. Given the invariant mentioned earlier, we assume there's been only one wraparound and so the xid whose value is larger precedes the other one (or, equivalently, the smaller xid belongs to a more recent transaction).

For 64-bit xids (Postgres type xid8), the regular integer comparison is used because those xids include the epoch number that tracks the number of xid wraparounds that have happened.

If any one or both arguments are 32-bit xids, the comparison is performed modulo-2^32, the same way it's done in Postgres: https://github.com/postgres/postgres/blob/302cf15759233e654512979286ce1a5c3b36625f/src/backend/access/transam/transam.c#L276-L293

Tests

iex> compare(3, 3) :eq

iex> compare(2, 1) :gt

iex> compare(2, 2) :eq

iex> compare(2, 3) :lt

iex> compare(4294967295, 4294967295) :eq

iex> compare(1, 2147483648) :lt

iex> compare(1, 2147483649) :lt

iex> compare(1, 2147483650) :gt

iex> compare(1, 4294967295) :gt

iex> compare(4294967295, 1) :lt

iex> compare(2147483648, 1) :gt

iex> compare(2147483649, 1) :lt

iex> compare(2147483648, 4294967295) :lt

iex> compare(2147483647, 4294967295) :lt

iex> compare(2147483646, 4294967295) :gt

Any of the two arguments can be 64-bit, the order doesn't matter:

iex> compare(1, 70866960384) :lt

iex> compare(1, 70866960385) :lt

iex> compare(1, 70866960386) :gt

iex> compare(70866960384, 1) :gt

iex> compare(70866960385, 1) :lt

When both numbers are 64-bit, regular comparison rules apply:

iex> compare(70866960386, 70866960385) :gt

iex> compare(70866960384, 73014444034) :lt

compare_snapshots(arg1, arg2)

@spec compare_snapshots(pg_snapshot(), pg_snapshot()) :: :lt | :eq | :gt

Compare two snapshots. Returns :lt if snapshot1 < snapshot2, :eq if equal, :gt if snapshot1 > snapshot2.

Comparison rules:

  • snapshot1 < snapshot2 if xmax1 < xmax2 OR (xmax1 == xmax2 AND xmin1 < xmin2)
  • snapshots are equal if both xmin and xmax are equal

Examples

iex> compare_snapshots({100, 200, []}, {150, 300, []})
:lt

iex> compare_snapshots({100, 300, []}, {150, 200, []})
:gt

iex> compare_snapshots({100, 300, []}, {150, 300, []})
:lt

iex> compare_snapshots({150, 300, []}, {100, 300, []})
:gt

iex> compare_snapshots({100, 300, [150]}, {100, 300, [200]})
:eq

iex> compare_snapshots({100, 300, []}, {100, 300, [150, 200, 250]})
:eq

is_eq(xid_l, xid_r)

(macro)

Guard function to check if xid_l == xid_r using the same wraparound logic as compare/2.

This can be used in guard clauses. For equality, two XIDs are equal if their difference is zero modulo 2^32.

Examples

iex> is_eq(3, 3)
true

iex> is_eq(2, 1)
false

iex> is_eq(2, 2)
true

iex> is_eq(2, 3)
false

iex> is_eq(4294967295, 4294967295)
true

iex> is_eq(1, 2147483648)
false

iex> is_eq(4294967295, 1)
false

Any of the two arguments can be 64-bit, the order doesn't matter:

iex> is_eq(1, 70866960384)
false

iex> is_eq(70866960384, 1)
false

# When both numbers are 64-bit, regular comparison rules apply:

iex> is_eq(70866960384, 70866960384)
true

iex> is_eq(70866960386, 70866960385)
false

is_lt(xid_l, xid_r)

(macro)

Guard function to check if xid_l < xid_r using the same wraparound logic as compare/2.

This can be used in guard clauses. Since guards don't allow bit-casting, we manually handle the modulo-2^32 arithmetic:

  • For 64-bit XIDs (both > 32-bit max), use regular comparison
  • For 32-bit XIDs, we compute the unsigned 32-bit difference and check if it's > 2^31

Examples

iex> is_lt(3, 3)
false

iex> is_lt(2, 1)
false

iex> is_lt(2, 2)
false

iex> is_lt(2, 3)
true

iex> is_lt(4294967295, 4294967295)
false

iex> is_lt(1, 2147483648)
true

iex> is_lt(1, 2147483649)
true

iex> is_lt(1, 2147483650)
false

iex> is_lt(1, 4294967295)
false

iex> is_lt(4294967295, 1)
true

iex> is_lt(2147483648, 1)
false

iex> is_lt(2147483649, 1)
true

iex> is_lt(2147483648, 4294967295)
true

iex> is_lt(2147483647, 4294967295)
true

iex> is_lt(2147483646, 4294967295)
false

Any of the two arguments can be 64-bit, the order doesn't matter:

iex> is_lt(1, 70866960384)
true

iex> is_lt(1, 70866960385)
true

iex> is_lt(1, 70866960386)
false

iex> is_lt(70866960384, 1)
false

iex> is_lt(70866960385, 1)
true

# When both numbers are 64-bit, regular comparison rules apply:

iex> is_lt(70866960386, 70866960385)
false

iex> is_lt(70866960384, 73014444034)
true