Adopting Elixir by Ben Marx
Author:Ben Marx
Language: eng
Format: epub
Tags: Pragmatic Bookshelf
Publisher: Pragmatic Bookshelf
Supervisors
Most new Elixir developers tend to think of supervisors in terms of fault tolerance because they provide the restart strategies that are the essential part of building reliable systems. Supervisors are so much more. They form the backbone of Elixir applications.
Ultimately, supervisors are responsible for how our processes start and shut down, whether an application is crashing and restarting or simply starting. Restarting of processes is optional, while starting and stopping them is essential. Let’s explore startup flow by addressing a bug in our PathAllocator implementation defined in Use GenServer as a Coordinator.
Starting a supervisor is a matter of defining all of its child specifications and then calling start_link. A child specification specifies exactly how the supervisor starts a child process, when and how many times to restart it, and how to shut it down. For a complete reference on child specifications, consult the Elixir documentation for Supervisors.[46]
We can start the PathAllocator example in Use GenServer as a Coordinator under a supervisor by defining a list of children, where each element is a tuple with the module name as first element and the argument given to start_link as second argument, like this:
children = [
{PathAllocator, System.tmp_dir}
]
Supervisor.start_link(children, strategy: :one_for_one)
Upon startup, a supervisor starts all of its children in the order they’re defined. Similarly, upon shutdown, the supervisor terminates all of its children in the reverse order. The initialization logic is in the init/1 function, but we haven’t defined the termination logic.
Whenever a supervisor restarts its children or the node shuts down, PathAllocator’s supervisor is going to send it an exit signal. By default, that exit signal will terminate the PathAllocator, regardless if it has processed all messages in its inbox or not, leaving spurious paths behind.
Let’s address this bug by cleaning up all files in the refs map on terminate. The first step is to trap exits with Process.flag(:trap_exit, true) on init, like this:
making_fun_transition/path_allocator_2.ex
def init(tmp_dir) do
Process.flag(:trap_exit, true)
{:ok, {tmp_dir, %{}}}
end
By trapping exits, if any external process causes the allocator to exit, the allocator won’t shut down immediately. Instead, it will run the terminate/2 callback. Next, we can write the termination logic, like this:
making_fun_transition/path_allocator_2.ex
def terminate(_reason, {_tmp_dir, refs}) do
for {_, path} <- refs do
File.rm(path)
end
:ok
end
The logic is dead simple. We simply remove every file in refs. This change guarantees PathAllocator will clean up all entries on shutdown or even when a bug causes part of your application to restart.
This example highlights the importance of proper termination of supervision trees in our applications. Once the supervisor sends the exit shutdown signal to the worker, the worker has 5 seconds to terminate, by default. If a given process requires more time to shutdown, you can specify the shutdown time when defining the process child specification.
Be careful, though. terminate/2 won’t happen in extreme scenarios, such as a spilled beer or a machine shutdown, so be defensive. For example, our PathAllocator’s init function could remove all files from the given directory to ensure a fresh start.
When
Download
This site does not store any files on its server. We only index and link to content provided by other sites. Please contact the content providers to delete copyright contents if any and email us, we'll remove relevant links or contents immediately.
Deep Learning with Python by François Chollet(12589)
Hello! Python by Anthony Briggs(9926)
OCA Java SE 8 Programmer I Certification Guide by Mala Gupta(9800)
The Mikado Method by Ola Ellnestam Daniel Brolund(9786)
Dependency Injection in .NET by Mark Seemann(9347)
Algorithms of the Intelligent Web by Haralambos Marmanis;Dmitry Babenko(8309)
Test-Driven iOS Development with Swift 4 by Dominik Hauser(7771)
Grails in Action by Glen Smith Peter Ledbrook(7704)
The Well-Grounded Java Developer by Benjamin J. Evans Martijn Verburg(7566)
Becoming a Dynamics 365 Finance and Supply Chain Solution Architect by Brent Dawson(7154)
Microservices with Go by Alexander Shuiskov(6919)
Practical Design Patterns for Java Developers by Miroslav Wengner(6833)
Test Automation Engineering Handbook by Manikandan Sambamurthy(6778)
Secrets of the JavaScript Ninja by John Resig Bear Bibeault(6423)
Angular Projects - Third Edition by Aristeidis Bampakos(6196)
The Art of Crafting User Stories by The Art of Crafting User Stories(5713)
NetSuite for Consultants - Second Edition by Peter Ries(5644)
Demystifying Cryptography with OpenSSL 3.0 by Alexei Khlebnikov(5455)
Kotlin in Action by Dmitry Jemerov(5073)
