From 1c330a4a52cf08eb9077eefef4d908f81c4024e9 Mon Sep 17 00:00:00 2001 From: Roman Kashitsyn Date: Thu, 20 Jun 2024 18:40:13 +0200 Subject: [PATCH] post 29: the plan-execute pattern (#75) Closes #67. --- about.tex | 4 +- images/29-dynamic-planning.svg | 274 +++++++++++++++++++++++++++++++++ images/29-just-do-it.svg | 120 +++++++++++++++ images/29-plan-execute.svg | 177 +++++++++++++++++++++ images/29-source.afdesign | Bin 0 -> 31287 bytes posts/28-enlightenmentware.tex | 6 +- posts/29-plan-execute.tex | 219 ++++++++++++++++++++++++++ 7 files changed, 795 insertions(+), 5 deletions(-) create mode 100644 images/29-dynamic-planning.svg create mode 100644 images/29-just-do-it.svg create mode 100644 images/29-plan-execute.svg create mode 100644 images/29-source.afdesign create mode 100644 posts/29-plan-execute.tex diff --git a/about.tex b/about.tex index d3fc016..7decd83 100644 --- a/about.tex +++ b/about.tex @@ -11,8 +11,8 @@ \section{about-author}{About the author} Hi there! My name is Roman Kashitsyn. -I'm a software engineer at \href{https://chainlinklabs.com/}{ChainLink Labs}, where I work on \href{https://chain.link/cross-chain}{\sc{ccip}}. -Before ChainLink Labs, I did hard-core software engineering at \href{https://dfinity.org}{\sc{dfinity}} and worked on large-scale distributed systems at \href{https://shopping.google.com/}{Google.Shopping} and \href{https://yandex.ru/maps}{Yandex.Maps}. +I'm a software engineer at \href{https://chainlinklabs.com/}{ChainLink Labs}, where I work on \href{https://chain.link/cross-chain}{\textsc{ccip}}. +Before ChainLink Labs, I did hard-core software engineering at \href{https://dfinity.org}{\textsc{dfinity}} and worked on large-scale distributed systems at \href{https://shopping.google.com/}{Google.Shopping} and \href{https://yandex.ru/maps}{Yandex.Maps}. \section{about-site}{About this website} diff --git a/images/29-dynamic-planning.svg b/images/29-dynamic-planning.svg new file mode 100644 index 0000000..c8a4d7b --- /dev/null +++ b/images/29-dynamic-planning.svg @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/29-just-do-it.svg b/images/29-just-do-it.svg new file mode 100644 index 0000000..521412b --- /dev/null +++ b/images/29-just-do-it.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/29-plan-execute.svg b/images/29-plan-execute.svg new file mode 100644 index 0000000..65c8417 --- /dev/null +++ b/images/29-plan-execute.svg @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/29-source.afdesign b/images/29-source.afdesign new file mode 100644 index 0000000000000000000000000000000000000000..0dec109898bdbc5b9e4704847c7d6d6467c0cb3f GIT binary patch literal 31287 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 qKBfO$wETB5{JR+bKPrZW6-qXv#rA8jy*, + outputs: Vec, + transform: Command, +} + +struct BuildGraph(\ldots) + +type BuildPlan = Vec; + +fn plan( + g: &BuildGraph, + cache: &BuildCache, + targets: &[ArtifactId], +) -> Result { \ldots } +\end{code} + +The most straightforward execution function would go over the tasks in the plan and execute them sequentially. + +\begin{code}[rust] +fn run_build_sequentially( + plan: &BuildPlan, + cache: &BuildCache, + sandbox: &Sandbox, +) -> Result<(), BuildError> { + for node in plan { + sandbox.execute(node, cache)?; + } + Ok(()) +} +\end{code} + +A more efficient approach is to implement pipeline parallelism and execute independent tasks concurrently. +This execution model adds a lot of complexity, which the build plan alone doesn't address. +So we introduce a state machine that keeps track of unfinished work and adjusts the plan as the execution unfolds. + +\begin{code}[rust] +struct ExecutionState { + graph: BuildGraph, + todo: Vec, + in_progress: Vec, + depcount: HashMap, +} + +enum Action { + Schedule(Vec), + Finish, +} + +impl ExecutionState { + \emph{// Returns the first action to take.} + fn init(&self) -> Action { \ldots } + + \emph{// Indicates that the task corresponding to the node is in progress.} + fn started(&must self, node: NodeId) { \ldots } + + \emph{// Indicates that the task corresponding to the node has completed.} + \emph{// Returns the next action to take.} + fn completed(&mut self, node: NodeId) -> Action { \ldots } +} +\end{code} + +The \code{run\_build} function is the driver loop that spawns processes and feeds their results to the state machine, which either outputs more tasks or reports that the build has finished. + +\begin{code}[rust] +fn run_build( + cache: &BuildCache, + sandbox: &Sandbox, + state: ExecutionState, +) -> Result<(), BuildError> { \ldots } +\end{code} + +This design splits a hard problem into smaller pieces that are easy to understand. +The most algorithm-heavy portions (planning and execution control) do not require any I/O and are easy to test. +Only the \code{run\_build} function needs to interact with the operating system. +The planning function is also helpful in implementing the \code{--dry-run} feature. + +\section{instances-relatives}{Instances and relatives} + +The \href{https://en.wikipedia.org/wiki/Query_plan}{\textsc{rdbms} query planner} is a supreme example of the pattern. +There are many ways to execute an \textsc{sql} query, and the database engine should always use the most efficient one. +Query plans give us insight into which path the engine is going to take; they are paramount for understanding and tweaking database performance. + +The \href{https://en.wikipedia.org/wiki/Interpreter_pattern}{interpreter pattern} is a special case of the plan-execute pattern, +where plans are syntactic trees expressing the intent, +and the execution stage is their interpretation. + +The \href{https://sans-io.readthedocs.io/how-to-sans-io.html}{Sans-\textsc{i/o}} protocol implementations provide a blueprint for extracting execution state machines from input/output driver loops +as discussed in the \href{#execution}{Execution} section. +This practice goes back to functional programming folklore; +\href{https://www.destroyallsoftware.com/talks/boundaries}{Gary Bernhardt} called it ``functional core, imperative shell''. + +Finally, all programs are plans: a programmer makes all decisions in advance and encodes them as a byte array, +leaving it to the computer to take care of the execution. +``Programming'' was a synonym of ``planning'' before computers conquered the world. + +\section{conclusion}{Conclusion} + +This article presented two approaches to implementing complex algorithms: +the ``just do it'' approach where decisions and actions are intertwined, +and the plan-execute pattern that separates decisions from actions. + +The former approach is the natural first choice that leads to straightforward code. +As our systems grow and become more complex, +the plan-execute pattern becomes a viable alternative that helps separate concerns and test our code more easily and thoroughly. + +\end{document}