Visualizing Elixir OTP Supervisor Strategies

Visualizing Elixir OTP Supervisor Strategies

June 16, 2023|
5 min read
  • elixir
  • otp
  • mermaid

Introduction

Seeking a hands-on way to further explore Mermaid — a remarkable JavaScript diagramming and charting tool — I decided to apply it to something familiar: visualizing OTP Supervisor strategies in Elixir. Mermaid's ability to render simple markup into great looking charts felt like an ideal fit for demonstrating these strategies.

The inspiration for this project came from the book Concurrent Data Processing in Elixir by Svilen Gospodinov, which I highly recommend. Within its pages, I found a series of diagrams representing Supervisor restart strategies. I set out to recreate these diagrams using Mermaid and take the opportunity to add Mermaid support to my blog. Moreover, I thought it would be beneficial to provide real-life examples demonstrating the appropriate use-cases for each strategy.

Mermaid's support extends to GitHub Markdown as well, making it an invaluable tool for maintaining diagrams within source control. This allows diagrams to evolve alongside the code as the architecture changes over time (as opposed to a static/binary image). I encourage you to take a peek at the Mermaid code I've provided below each diagram — you might find that it is easy to grasp and use.

So, with that said, let's dive into these graphical representations of OTP Supervisor strategies built with Mermaid.

:one_for_one

This strategy restarts only the child process that has failed, leaving the other child processes running. This is useful when each child process is independent, and their failures do not affect each other.

Example

A web server that needs to handle multiple client connections. If one connection fails, only that connection needs to be restarted.

Diagram

flowchart LR classDef default fill:white,stroke:green,stroke-width:1px classDef restarted fill:green,color:#fff classDef killed stroke:red,stroke-dasharray: 5 5 subgraph BEFORE bs(Supervisor) --> ba(A) bs(Supervisor) --> bb(B):::killed bs(Supervisor) --> bc(C) end subgraph AFTER sa(Supervisor) --> aa(A) sa(Supervisor) --> ab(B):::restarted sa(Supervisor) --> ac(C) end BEFORE --> AFTER
Mermaid Code
flowchart LR
  classDef default fill:white,stroke:green,stroke-width:1px
  classDef restarted fill:green,color:#fff
  classDef killed stroke:red,stroke-dasharray: 5 5


  subgraph BEFORE
    bs(Supervisor) --> ba(A)
    bs(Supervisor) --> bb(B):::killed
    bs(Supervisor) --> bc(C)
  end

  subgraph AFTER
    sa(Supervisor) --> aa(A)
    sa(Supervisor) --> ab(B):::restarted
    sa(Supervisor) --> ac(C)
  end

BEFORE --> AFTER

:one_for_all

This strategy restarts all child processes when one of them fails. This is useful when the child processes are dependent on each other and cannot function correctly if one of them fails.

Examples

  • A distributed system that consists of multiple nodes. If one node goes down, the entire system may stop functioning correctly. A supervisor with :one_for_all strategy can be used to restart all the nodes if one of them fails.
  • A data processing system that passes state between processes.

Diagram

flowchart LR classDef default fill:white,stroke:green,stroke-width:1px classDef restarted fill:green,color:#fff classDef killed stroke:red,stroke-dasharray: 5 5 subgraph BEFORE bs(Supervisor) --> ba(A) bs(Supervisor) --> bb(B):::killed bs(Supervisor) --> bc(C) end subgraph AFTER sa(Supervisor) --> aa(A):::restarted sa(Supervisor) --> ab(B):::restarted sa(Supervisor) --> ac(C):::restarted end BEFORE --> AFTER
Mermaid Code
flowchart LR
  classDef default fill:white,stroke:green,stroke-width:1px
  classDef restarted fill:green,color:#fff
  classDef killed stroke:red,stroke-dasharray: 5 5


  subgraph BEFORE
    bs(Supervisor) --> ba(A)
    bs(Supervisor) --> bb(B):::killed
    bs(Supervisor) --> bc(C)
  end

  subgraph AFTER
    sa(Supervisor) --> aa(A):::restarted
    sa(Supervisor) --> ab(B):::restarted
    sa(Supervisor) --> ac(C):::restarted
  end

BEFORE --> AFTER

:rest_for_one

This strategy restarts the failed child process and all child processes that were started after it. This is useful when child processes have dependencies on each other and need to be started in a specific order.

Examples

  • A web application that consists of multiple processes, such as a web server, a database connection process, and a cache process. If the database connection process fails, the cache process that depends on it will also fail. A supervisor with :rest_for_one strategy can be used to restart the database connection process and all the processes that depend on it.
  • A system that consists of multiple processes that need to be started in a specific order. For example, a system that processes financial transactions may have multiple processes that need to be started in a certain order, such as a process that reads transaction data from a file, a process that performs validation on the data, and a process that updates the database. A supervisor with :rest_for_one strategy can be used to ensure that all the processes are started in the correct order and restarted if one of them fails.

Diagram

flowchart LR classDef default fill:white,stroke:green,stroke-width:1px classDef restarted fill:green,color:#fff classDef killed stroke:red,stroke-dasharray: 5 5 subgraph BEFORE bs(Supervisor) --> ba(A) bs(Supervisor) --> bb(B):::killed bs(Supervisor) --> bc(C) end subgraph AFTER sa(Supervisor) --> aa(A) sa(Supervisor) --> ab(B):::restarted sa(Supervisor) --> ac(C):::restarted end BEFORE --> AFTER
Mermaid Code
flowchart LR
  classDef default fill:white,stroke:green,stroke-width:1px
  classDef restarted fill:green,color:#fff
  classDef killed stroke:red,stroke-dasharray: 5 5


  subgraph BEFORE
    bs(Supervisor) --> ba(A)
    bs(Supervisor) --> bb(B):::killed
    bs(Supervisor) --> bc(C)
  end

  subgraph AFTER
    sa(Supervisor) --> aa(A)
    sa(Supervisor) --> ab(B):::restarted
    sa(Supervisor) --> ac(C):::restarted
  end

BEFORE --> AFTER

Image Credits

Photo by Prachi Gautam on Unsplash


© 2023, Dorian Karter