Packages
altworx_runbox
21.2.0
25.0.0
24.0.0
23.1.0
23.0.0
22.2.0
22.1.0
22.0.0
21.2.0
21.1.2
21.1.1
21.1.0
21.0.0
20.0.0
19.0.0
18.0.0
17.2.0
17.1.0
17.0.1
17.0.0
16.2.0
16.1.0
16.0.0
15.0.0
14.1.0
14.0.1
14.0.0
13.0.3
13.0.2
13.0.1
13.0.0
12.1.0
12.0.0
11.0.1
11.0.0
10.0.0
9.0.0
8.0.0
7.0.1
7.0.0
6.0.0
5.0.0
4.0.0
3.0.0
2.1.0
2.0.0
1.4.1
1.4.0
1.3.0
1.2.0
1.1.0
1.0.0
0.1.3
0.1.2
0.1.1
0.1.0
Runbox is a library for running Altworx scenarios.
Current section
Files
Jump to
Current section
Files
lib/runbox/state_store/savepoint.ex
defmodule Runbox.StateStore.Savepoint do
@moduledoc group: :internal
@moduledoc """
Savepoint represents state of runtime run valid to scheduled timestamp.
"""
alias __MODULE__, as: Savepoint
alias Runbox.Runtime.Stage.UnitRegistry
alias Runbox.StateStore.Entity
alias Runbox.StateStore.ScheduleUtils
defstruct timestamp: 0,
stats: %{registered: 0, saved: 0},
entity_registry: %{}
@type t :: %Savepoint{
timestamp: ScheduleUtils.epoch_ms(),
stats: %{registered: integer, saved: integer},
entity_registry: %{Entity.id() => :not_set_yet | Entity.state()}
}
@doc """
Creates new savepoint with given timestamp and register entities.
"""
@spec new([Entity.id()], ScheduleUtils.epoch_ms()) :: t
def new(entity_ids, timestamp) do
%Savepoint{
timestamp: timestamp,
entity_registry: Map.new(entity_ids, fn entity_id -> {entity_id, :not_set_yet} end),
stats: %{registered: Enum.count(entity_ids), saved: 0}
}
end
@doc """
Returns savepoint timestamp.
"""
@spec timestamp(t) :: ScheduleUtils.epoch_ms()
def timestamp(%Savepoint{} = savepoint) do
savepoint.timestamp
end
@doc """
Returns all registered entity ids.
"""
@spec registered_entities(t) :: [Entity.id()]
def registered_entities(%Savepoint{} = savepoint) do
Map.keys(savepoint.entity_registry)
end
@doc """
Saves entity state in given savepoint.
"""
@spec save(t, Entity.id(), Entity.state()) :: t
def save(%Savepoint{entity_registry: entity_registry} = savepoint, entity_id, entity_state) do
case Map.fetch(entity_registry, entity_id) do
{:ok, :not_set_yet} ->
state = erase_nonpersisted_values(entity_state)
entity_registry = Map.put(entity_registry, entity_id, state)
stats = %{savepoint.stats | saved: savepoint.stats.saved + 1}
%Savepoint{savepoint | entity_registry: entity_registry, stats: stats}
{:ok, _} ->
# entity state has been already set = ignore
savepoint
:error ->
# entity is not registered = ignore
savepoint
end
end
@doc """
Returns entities with saved states.
"""
@spec entity_states(t) :: [{Entity.id(), Entity.state()}]
def entity_states(%Savepoint{entity_registry: entity_registry}) do
Map.to_list(entity_registry)
end
@doc """
Predicate returning true when all registered states has been saved.
"""
@spec all_saved?(t) :: boolean
def all_saved?(%Savepoint{stats: %{registered: r, saved: s}}) do
r == s
end
@doc """
Returns registered entities count of given savepoint.
"""
@spec registered_entities_count(t) :: integer
def registered_entities_count(%Savepoint{stats: %{registered: r}}) do
r
end
# public only for tests
@doc false
@spec fetch_entity_state(t, Entity.id()) :: {:ok, :not_set_yet | Entity.state()} | :error
def fetch_entity_state(%Savepoint{} = savepoint, entity_id) do
Map.fetch(savepoint.entity_registry, entity_id)
end
# public only for tests
@doc false
@spec saved_entities_count(t) :: integer
def saved_entities_count(%Savepoint{stats: %{saved: s}}) do
s
end
defp erase_nonpersisted_values(%UnitRegistry{} = registry) do
%UnitRegistry{registry | parse_msg_fns: nil, register_unit_fns: nil}
end
defp erase_nonpersisted_values(other), do: other
end