Split cell status into validity status and evaluation status

This commit is contained in:
Jonatan Kłosko 2021-01-14 21:18:18 +01:00
parent 8beeb48d1b
commit 401f18fecc
2 changed files with 64 additions and 43 deletions

View file

@ -41,14 +41,16 @@ defmodule LiveBook.Session.Data do
} }
@type cell_info :: %{ @type cell_info :: %{
status: cell_status(), validity_status: cell_validity_status(),
evaluation_status: cell_evaluation_status(),
revision: non_neg_integer(), revision: non_neg_integer(),
# TODO: specify it's a list of deltas, once defined # TODO: specify it's a list of deltas, once defined
deltas: list(), deltas: list(),
evaluated_at: DateTime.t() evaluated_at: DateTime.t()
} }
@type cell_status :: :fresh | :queued | :evaluating | :evaluated | :stale @type cell_validity_status :: :fresh | :evaluated | :stale
@type cell_evaluation_status :: :ready | :queued | :evaluating
@type index :: non_neg_integer() @type index :: non_neg_integer()
@ -139,7 +141,7 @@ defmodule LiveBook.Session.Data do
def apply_operation(data, {:delete_cell, id}) do def apply_operation(data, {:delete_cell, id}) do
with {:ok, cell, section} <- Notebook.fetch_cell_and_section(data.notebook, id) do with {:ok, cell, section} <- Notebook.fetch_cell_and_section(data.notebook, id) do
case data.cell_infos[cell.id].status do case data.cell_infos[cell.id].evaluation_status do
:evaluating -> :evaluating ->
data data
|> with_actions() |> with_actions()
@ -166,7 +168,7 @@ defmodule LiveBook.Session.Data do
def apply_operation(data, {:queue_cell_evaluation, id}) do def apply_operation(data, {:queue_cell_evaluation, id}) do
with {:ok, cell, section} <- Notebook.fetch_cell_and_section(data.notebook, id), with {:ok, cell, section} <- Notebook.fetch_cell_and_section(data.notebook, id),
:elixir <- cell.type, :elixir <- cell.type,
false <- data.cell_infos[cell.id].status in [:queued, :evaluating] do :ready <- data.cell_infos[cell.id].evaluation_status do
data data
|> with_actions() |> with_actions()
|> queue_prerequisite_cells_evaluation(cell, section) |> queue_prerequisite_cells_evaluation(cell, section)
@ -179,16 +181,20 @@ defmodule LiveBook.Session.Data do
end end
def apply_operation(data, {:add_cell_evaluation_stdout, id, string}) do def apply_operation(data, {:add_cell_evaluation_stdout, id, string}) do
with {:ok, cell, _} <- Notebook.fetch_cell_and_section(data.notebook, id) do with {:ok, cell, _} <- Notebook.fetch_cell_and_section(data.notebook, id),
:evaluating <- data.cell_infos[cell.id].evaluation_status do
data data
|> with_actions() |> with_actions()
|> add_cell_evaluation_stdout(cell, string) |> add_cell_evaluation_stdout(cell, string)
|> wrap_ok() |> wrap_ok()
else
_ -> :error
end end
end end
def apply_operation(data, {:add_cell_evaluation_response, id, response}) do def apply_operation(data, {:add_cell_evaluation_response, id, response}) do
with {:ok, cell, section} <- Notebook.fetch_cell_and_section(data.notebook, id) do with {:ok, cell, section} <- Notebook.fetch_cell_and_section(data.notebook, id),
:evaluating <- data.cell_infos[cell.id].evaluation_status do
data data
|> with_actions() |> with_actions()
|> add_cell_evaluation_response(cell, response) |> add_cell_evaluation_response(cell, response)
@ -196,12 +202,14 @@ defmodule LiveBook.Session.Data do
|> mark_dependent_cells_as_stale(cell) |> mark_dependent_cells_as_stale(cell)
|> maybe_evaluate_queued() |> maybe_evaluate_queued()
|> wrap_ok() |> wrap_ok()
else
_ -> :error
end end
end end
def apply_operation(data, {:cancel_cell_evaluation, id}) do def apply_operation(data, {:cancel_cell_evaluation, id}) do
with {:ok, cell, section} <- Notebook.fetch_cell_and_section(data.notebook, id) do with {:ok, cell, section} <- Notebook.fetch_cell_and_section(data.notebook, id) do
case data.cell_infos[cell.id].status do case data.cell_infos[cell.id].evaluation_status do
:evaluating -> :evaluating ->
data data
|> with_actions() |> with_actions()
@ -273,7 +281,7 @@ defmodule LiveBook.Session.Data do
|> update_section_info!(section.id, fn section -> |> update_section_info!(section.id, fn section ->
%{section | evaluation_queue: section.evaluation_queue ++ [cell.id]} %{section | evaluation_queue: section.evaluation_queue ++ [cell.id]}
end) end)
|> set_cell_info!(cell.id, status: :queued) |> set_cell_info!(cell.id, evaluation_status: :queued)
end end
defp unqueue_cell_evaluation(data_actions, cell, section) do defp unqueue_cell_evaluation(data_actions, cell, section) do
@ -281,7 +289,7 @@ defmodule LiveBook.Session.Data do
|> update_section_info!(section.id, fn section -> |> update_section_info!(section.id, fn section ->
%{section | evaluation_queue: List.delete(section.evaluation_queue, cell.id)} %{section | evaluation_queue: List.delete(section.evaluation_queue, cell.id)}
end) end)
|> set_cell_info!(cell.id, status: :stale) |> set_cell_info!(cell.id, evaluation_status: :ready)
end end
defp add_cell_evaluation_stdout({data, _} = data_actions, _cell, _string) do defp add_cell_evaluation_stdout({data, _} = data_actions, _cell, _string) do
@ -302,28 +310,22 @@ defmodule LiveBook.Session.Data do
defp finish_cell_evaluation(data_actions, cell, section) do defp finish_cell_evaluation(data_actions, cell, section) do
data_actions data_actions
|> set_cell_info!(cell.id, status: :evaluated, evaluated_at: DateTime.utc_now()) |> set_cell_info!(cell.id,
validity_status: :evaluated,
evaluation_status: :ready,
evaluated_at: DateTime.utc_now()
)
|> set_section_info!(section.id, evaluating_cell_id: nil) |> set_section_info!(section.id, evaluating_cell_id: nil)
end end
defp mark_dependent_cells_as_stale({data, _} = data_actions, cell) do defp mark_dependent_cells_as_stale({data, _} = data_actions, cell) do
invalidated_cells = child_cells_with_status(data, cell, :evaluated) invalidated_cells =
data.notebook
|> Notebook.child_cells(cell.id)
|> Enum.filter(fn cell -> data.cell_infos[cell.id].validity_status == :evaluated end)
data_actions data_actions
|> reduce(invalidated_cells, &set_cell_info!(&1, &2.id, status: :stale)) |> reduce(invalidated_cells, &set_cell_info!(&1, &2.id, validity_status: :stale))
end
defp fresh_parent_cells_queue(data, cell) do
data.notebook
|> Notebook.parent_cells(cell.id)
|> Enum.take_while(fn parent -> data.cell_infos[parent.id].status == :fresh end)
|> Enum.reverse()
end
defp child_cells_with_status(data, cell, status) do
data.notebook
|> Notebook.child_cells(cell.id)
|> Enum.filter(fn cell -> data.cell_infos[cell.id].status == status end)
end end
# If there are idle sections with non-empty evaluation queue, # If there are idle sections with non-empty evaluation queue,
@ -338,7 +340,7 @@ defmodule LiveBook.Session.Data do
data_actions data_actions
|> set!(notebook: Notebook.update_cell(data.notebook, id, &%{&1 | outputs: []})) |> set!(notebook: Notebook.update_cell(data.notebook, id, &%{&1 | outputs: []}))
|> set_cell_info!(id, status: :evaluating) |> set_cell_info!(id, evaluation_status: :evaluating)
|> set_section_info!(section.id, evaluating_cell_id: id, evaluation_queue: ids) |> set_section_info!(section.id, evaluating_cell_id: id, evaluation_queue: ids)
|> add_action({:start_evaluation, cell, section}) |> add_action({:start_evaluation, cell, section})
@ -351,19 +353,32 @@ defmodule LiveBook.Session.Data do
defp clear_section_evaluation(data_actions, section) do defp clear_section_evaluation(data_actions, section) do
data_actions data_actions
|> set_section_info!(section.id, evaluating_cell_id: nil, evaluation_queue: []) |> set_section_info!(section.id, evaluating_cell_id: nil, evaluation_queue: [])
|> reduce(section.cells, &set_cell_info!(&1, &2.id, status: :fresh)) |> reduce(
section.cells,
&set_cell_info!(&1, &2.id, validity_status: :fresh, evaluation_status: :ready)
)
|> add_action({:stop_evaluation, section}) |> add_action({:stop_evaluation, section})
end end
defp queue_prerequisite_cells_evaluation({data, _} = data_actions, cell, section) do defp queue_prerequisite_cells_evaluation({data, _} = data_actions, cell, section) do
prerequisites_queue = fresh_parent_cells_queue(data, cell) prerequisites_queue =
data.notebook
|> Notebook.parent_cells(cell.id)
|> Enum.take_while(fn parent ->
info = data.cell_infos[parent.id]
info.validity_status == :fresh and info.evaluation_status == :ready
end)
|> Enum.reverse()
data_actions data_actions
|> reduce(prerequisites_queue, &queue_cell_evaluation(&1, &2, section)) |> reduce(prerequisites_queue, &queue_cell_evaluation(&1, &2, section))
end end
defp unqueue_dependent_cells_evaluation({data, _} = data_actions, cell, section) do defp unqueue_dependent_cells_evaluation({data, _} = data_actions, cell, section) do
queued_dependent_cells = child_cells_with_status(data, cell, :queued) queued_dependent_cells =
data.notebook
|> Notebook.child_cells(cell.id)
|> Enum.filter(fn cell -> data.cell_infos[cell.id].evaluation_status == :queued end)
data_actions data_actions
|> reduce(queued_dependent_cells, &unqueue_cell_evaluation(&1, &2, section)) |> reduce(queued_dependent_cells, &unqueue_cell_evaluation(&1, &2, section))
@ -384,7 +399,8 @@ defmodule LiveBook.Session.Data do
%{ %{
revision: 0, revision: 0,
deltas: [], deltas: [],
status: :fresh, validity_status: :fresh,
evaluation_status: :ready,
evaluated_at: nil evaluated_at: nil
} }
end end

View file

@ -94,7 +94,7 @@ defmodule LiveBook.Session.DataTest do
assert {:ok, assert {:ok,
%{ %{
cell_infos: %{"c2" => %{status: :fresh}}, cell_infos: %{"c2" => %{evaluation_status: :ready}},
section_infos: %{"s1" => %{evaluating_cell_id: nil, evaluation_queue: []}} section_infos: %{"s1" => %{evaluating_cell_id: nil, evaluation_queue: []}}
}, _actions} = Data.apply_operation(data, operation) }, _actions} = Data.apply_operation(data, operation)
end end
@ -154,7 +154,7 @@ defmodule LiveBook.Session.DataTest do
assert {:ok, assert {:ok,
%{ %{
cell_infos: %{"c2" => %{status: :stale}} cell_infos: %{"c2" => %{validity_status: :stale}}
}, _actions} = Data.apply_operation(data, operation) }, _actions} = Data.apply_operation(data, operation)
end end
@ -213,7 +213,7 @@ defmodule LiveBook.Session.DataTest do
assert {:ok, assert {:ok,
%{ %{
cell_infos: %{"c1" => %{status: :evaluating}}, cell_infos: %{"c1" => %{evaluation_status: :evaluating}},
section_infos: %{"s1" => %{evaluating_cell_id: "c1", evaluation_queue: []}} section_infos: %{"s1" => %{evaluating_cell_id: "c1", evaluation_queue: []}}
}, _actions} = Data.apply_operation(data, operation) }, _actions} = Data.apply_operation(data, operation)
end end
@ -244,7 +244,7 @@ defmodule LiveBook.Session.DataTest do
assert {:ok, assert {:ok,
%{ %{
cell_infos: %{"c2" => %{status: :queued}}, cell_infos: %{"c2" => %{evaluation_status: :queued}},
section_infos: %{"s1" => %{evaluating_cell_id: "c1", evaluation_queue: ["c2"]}} section_infos: %{"s1" => %{evaluating_cell_id: "c1", evaluation_queue: ["c2"]}}
}, []} = Data.apply_operation(data, operation) }, []} = Data.apply_operation(data, operation)
end end
@ -274,7 +274,7 @@ defmodule LiveBook.Session.DataTest do
assert {:ok, assert {:ok,
%{ %{
cell_infos: %{"c1" => %{status: :evaluated}}, cell_infos: %{"c1" => %{validity_status: :evaluated, evaluation_status: :ready}},
section_infos: %{"s1" => %{evaluating_cell_id: nil, evaluation_queue: []}} section_infos: %{"s1" => %{evaluating_cell_id: nil, evaluation_queue: []}}
}, []} = Data.apply_operation(data, operation) }, []} = Data.apply_operation(data, operation)
end end
@ -293,7 +293,7 @@ defmodule LiveBook.Session.DataTest do
assert {:ok, assert {:ok,
%{ %{
cell_infos: %{"c2" => %{status: :evaluating}}, cell_infos: %{"c2" => %{evaluation_status: :evaluating}},
section_infos: %{"s1" => %{evaluating_cell_id: "c2", evaluation_queue: []}} section_infos: %{"s1" => %{evaluating_cell_id: "c2", evaluation_queue: []}}
}, },
[{:start_evaluation, %{id: "c2"}, %{id: "s1"}}]} = [{:start_evaluation, %{id: "c2"}, %{id: "s1"}}]} =
@ -313,8 +313,8 @@ defmodule LiveBook.Session.DataTest do
assert {:ok, assert {:ok,
%{ %{
cell_infos: %{ cell_infos: %{
"c1" => %{status: :evaluating}, "c1" => %{evaluation_status: :evaluating},
"c2" => %{status: :queued} "c2" => %{evaluation_status: :queued}
}, },
section_infos: %{"s1" => %{evaluating_cell_id: "c1", evaluation_queue: ["c2"]}} section_infos: %{"s1" => %{evaluating_cell_id: "c1", evaluation_queue: ["c2"]}}
}, },
@ -341,7 +341,7 @@ defmodule LiveBook.Session.DataTest do
assert {:ok, assert {:ok,
%{ %{
cell_infos: %{"c2" => %{status: :stale}} cell_infos: %{"c2" => %{validity_status: :stale}}
}, []} = Data.apply_operation(data, operation) }, []} = Data.apply_operation(data, operation)
end end
end end
@ -380,8 +380,13 @@ defmodule LiveBook.Session.DataTest do
assert {:ok, assert {:ok,
%{ %{
cell_infos: %{"c1" => %{status: :fresh}, "c2" => %{status: :fresh}}, cell_infos: %{
section_infos: %{"s1" => %{evaluating_cell_id: nil, evaluation_queue: []}} "c1" => %{validity_status: :fresh},
"c2" => %{validity_status: :fresh}
},
section_infos: %{
"s1" => %{evaluating_cell_id: nil, evaluation_queue: []}
}
}, _actions} = Data.apply_operation(data, operation) }, _actions} = Data.apply_operation(data, operation)
end end
@ -415,7 +420,7 @@ defmodule LiveBook.Session.DataTest do
assert {:ok, assert {:ok,
%{ %{
cell_infos: %{"c2" => %{status: :stale}}, cell_infos: %{"c2" => %{validity_status: :fresh, evaluation_status: :ready}},
section_infos: %{"s1" => %{evaluating_cell_id: "c1", evaluation_queue: []}} section_infos: %{"s1" => %{evaluating_cell_id: "c1", evaluation_queue: []}}
}, []} = Data.apply_operation(data, operation) }, []} = Data.apply_operation(data, operation)
end end
@ -436,7 +441,7 @@ defmodule LiveBook.Session.DataTest do
assert {:ok, assert {:ok,
%{ %{
cell_infos: %{"c3" => %{status: :stale}}, cell_infos: %{"c3" => %{evaluation_status: :ready}},
section_infos: %{"s1" => %{evaluating_cell_id: "c1", evaluation_queue: []}} section_infos: %{"s1" => %{evaluating_cell_id: "c1", evaluation_queue: []}}
}, []} = Data.apply_operation(data, operation) }, []} = Data.apply_operation(data, operation)
end end