diff --git a/lib/anoma/storage.ex b/lib/anoma/storage.ex index a1ec9bab5..adebc0922 100644 --- a/lib/anoma/storage.ex +++ b/lib/anoma/storage.ex @@ -35,7 +35,16 @@ defmodule Anoma.Storage do Please see my testing module `AnomaTest.Storage` to learn more on how to use me + + ### Snapshots + One can snapshot the keys provided in the code by running the following + + - `snapshot_order/1` + - `put_snapshot/2` + - `in_snapshot/2` + - `get_at_snapshot/2` """ + use TypedStruct @typedoc """ @@ -64,6 +73,8 @@ defmodule Anoma.Storage do @type qualified_key() :: nonempty_improper_list(any(), non_neg_integer()) @type qualified_value() :: any() + @type snapshot() :: {t(), list({order_key(), non_neg_integer()})} + ############################################################ # Creation API # ############################################################ @@ -169,6 +180,44 @@ defmodule Anoma.Storage do end end + ############################################################ + # Snapshots # + ############################################################ + + @spec snapshot_order(t()) :: result(snapshot()) + def snapshot_order(storage) do + :mnesia.transaction(fn -> + snapshot = [{{:"$1", :"$2", :"$3"}, [], [{{:"$2", :"$3"}}]}] + {storage, :mnesia.select(storage.order, snapshot)} + end) + end + + @spec put_snapshot(t(), order_key()) :: result(:ok) + def put_snapshot(storage, key) do + with {:atomic, snapshot} <- snapshot_order(storage) do + put(storage, key, snapshot) + end + end + + @spec in_snapshot(snapshot(), order_key()) :: nil | non_neg_integer() + def in_snapshot({_, snapshot}, key) do + List.keyfind(snapshot, key, 0, {nil, nil}) + |> elem(1) + end + + @spec get_at_snapshot(snapshot(), order_key()) :: + :absent | {:ok, qualified_value()} + def get_at_snapshot(snapshot = {storage, _}, key) do + position = in_snapshot(snapshot, key) + + with {:atomic, [{_, [^position, ^key | 0], value}]} <- + read_at_order(storage, key, position) do + {:ok, value} + else + _ -> :absent + end + end + ############################################################ # Queries # ############################################################ diff --git a/test/storage_test.exs b/test/storage_test.exs index 1db0de055..f14c31f4f 100644 --- a/test/storage_test.exs +++ b/test/storage_test.exs @@ -97,4 +97,31 @@ defmodule AnomaTest.Storage do {:atomic, [{storage.order, testing_atom, 3}]} end end + + describe "Snapshots" do + test "snapshots properly put", %{storage: storage} do + snapshot_storage = :snapshot_super_secret + assert {:atomic, :ok} = Storage.put_snapshot(storage, snapshot_storage) + assert {:ok, _} = Storage.get(storage, snapshot_storage) + end + + test "snapshots properly get the latest", %{storage: storage} do + snapshot_storage = :super_hot + testing_atom = 111_222_333_444_555_666 + Storage.write_at_order(storage, testing_atom, 10, 3) + assert {:atomic, :ok} = Storage.put_snapshot(storage, snapshot_storage) + assert {:ok, snapshot} = Storage.get(storage, snapshot_storage) + assert Storage.in_snapshot(snapshot, testing_atom) == 3 + + assert Storage.get_at_snapshot(snapshot, testing_atom) == + {:ok, 10} + end + + test "missing key gives us nil", %{storage: storage} do + snapshot_storage = :super_hot_hot + nonsense_atom = :very_good_atom + assert {:atomic, snapshot} = Storage.snapshot_order(storage) + assert Storage.get_at_snapshot(snapshot, nonsense_atom) + end + end end