improvement: use a guaranteed-last ulid for balance_as_of calculation

This commit is contained in:
Zach Daniel 2024-06-23 11:59:17 -04:00
parent 429d53472e
commit f543bc80f5
2 changed files with 42 additions and 1 deletions

View file

@ -12,6 +12,6 @@ defmodule AshDoubleEntry.Account.Calculations.BalanceAsOf do
@doc false
def ulid(timestamp) do
AshDoubleEntry.ULID.generate(timestamp)
AshDoubleEntry.ULID.generate_last(timestamp)
end
end

View file

@ -77,6 +77,31 @@ defmodule AshDoubleEntry.ULID do
ulid
end
@doc """
Generates a Crockford Base32 encoded ULID, guaranteed to sort equal to or after any other ULID generated for the same timestamp.
Do not use this for storage, only for generating comparators, i.e "balance as of a given ulid".
If a value is provided for `timestamp`, the generated ULID will be for the provided timestamp.
Otherwise, a ULID will be generated for the current time.
Arguments:
* `timestamp`: A Unix timestamp with millisecond precision.
"""
def generate_last(timestamp \\ System.system_time(:millisecond))
def generate_last(%DateTime{} = datetime) do
datetime
|> DateTime.to_unix(:millisecond)
|> generate_last()
end
def generate_last(timestamp) do
{:ok, ulid} = encode(bingenerate_last(timestamp))
ulid
end
@doc """
Generates a binary ULID.
@ -91,6 +116,22 @@ defmodule AshDoubleEntry.ULID do
<<timestamp::unsigned-size(48), :crypto.strong_rand_bytes(10)::binary>>
end
@doc """
Generates a binary ULID.
Do not use this for storage, only for generating comparators, i.e "balance as of a given ulid".
If a value is provided for `timestamp`, the generated ULID will be for the provided timestamp.
Otherwise, a ULID will be generated for the current time.
Arguments:
* `timestamp`: A Unix timestamp with millisecond precision.
"""
def bingenerate_last(timestamp \\ System.system_time(:millisecond)) do
<<timestamp::unsigned-size(48), 255, 255, 255, 255, 255, 255, 255, 255, 255, 255>>
end
@doc false
def encode(
<<b1::3, b2::5, b3::5, b4::5, b5::5, b6::5, b7::5, b8::5, b9::5, b10::5, b11::5, b12::5,