contents

Front matter

foreword

preface

acknowledgments

about this book

about the author

about the cover illustration

Part 1 Functional Elixir

1 First steps

1.1 About Erlang

High availability

Erlang concurrency

Server-side systems

The development platform

Relationship to microservices

1.2 About Elixir

Code simplification

Composing functions

The big picture

1.3 Disadvantages

Speed

Ecosystem

2 Building blocks

2.1 The interactive shell

2.2 Working with variables

2.3 Organizing your code

Modules

Functions

Function arity

Function visibility

Imports and aliases

Module attributes

Comments

2.4 Understanding the type system

Numbers

Atoms

Tuples

Lists

Immutability

Maps

Binaries and bitstrings

Strings

First-class functions

Other built-in types

Higher-level types

IO lists

2.5 Operators

2.6 Macros

2.7 Understanding the runtime

Modules and functions in the runtime

Starting the runtime

3 Control flow

3.1 Pattern matching

The match operator

Matching tuples

Matching constants

Variables in patterns

Matching lists

Matching maps

Matching bitstrings and binaries

Compound matches

General behavior

3.2 Matching with functions

Multiclause functions

Guards

Multiclause lambdas

3.3 Conditionals

Branching with multiclause functions

Classical branching expressions

The with expression

3.4 Loops and iterations

Iterating with recursion

Tail function calls

Higher-order functions

Comprehensions

Streams

4 Data abstractions

4.1 Abstracting with modules

Basic abstraction

Composing abstractions

Structuring data with maps

Abstracting with structs

Data transparency

4.2 Working with hierarchical data

Generating IDs

Updating entries

Immutable hierarchical updates

Iterative updates

Exercise: Importing from a file

4.3 Polymorphism with protocols

Protocol basics

Implementing a protocol

Built-in protocols

Part 2 Concurrent Elixir

5 Concurrency primitives

5.1 Concurrency in BEAM

5.2 Working with processes

Creating processes

Message passing

5.3 Stateful server processes

Server processes

Keeping a process state

Mutable state

Complex states

Registered processes

5.4 Runtime considerations

A process is sequential

Unlimited process mailboxes

Shared-nothing concurrency

Scheduler inner workings

6 Generic server processes

6.1 Building a generic server process

Plugging in with modules

Implementing the generic code

Using the generic abstraction

Supporting asynchronous requests

Exercise: Refactoring the to-do server

6.2 Using GenServer

OTP behaviours

Plugging into GenServer

Handling requests

Handling plain messages

Other GenServer features

Process life cycle

OTP-compliant processes

Exercise: GenServer-powered to-do server

7 Building a concurrent system

7.1 Working with the Mix project

7.2 Managing multiple to-do lists

Implementing a cache

Writing tests

Analyzing process dependencies

7.3 Persisting data

Encoding and persisting

Using the database

Analyzing the system

Addressing the process bottleneck

Exercise: Pooling and synchronizing

7.4 Reasoning with processes

8 Fault tolerance basics

8.1 Run-time errors

Error types

Handling errors

8.2 Errors in concurrent systems

Linking processes

Monitors

8.3 Supervisors

Preparing the existing code

Starting the supervisor process

Child specification

Wrapping the supervisor

Using a callback module

Linking all processes

Restart frequency

9 Isolating error effects

9.1 Supervision trees

Separating loosely dependent parts

Rich process discovery

Via tuples

Registering database workers

Supervising database workers

Organizing the supervision tree

9.2 Starting processes dynamically

Registering to-do servers

Dynamic supervision

Finding to-do servers

Using the temporary restart strategy

Testing the system

9.3 “Let it crash”

Processes that shouldn’t crash

Handling expected errors

Preserving the state

10 Beyond GenServer

10.1 Tasks

Awaited tasks

Non-awaited tasks

Supervising dynamic tasks

10.2 Agents

Basic use

Agents and concurrency

Agent-powered to-do server

Limitations of agents

10.3 ETS tables

Basic operations

ETS-powered key-value store

Other ETS operations

Exercise: Process registry

Part 3 Production

11 Working with components

11.1 OTP applications

Creating applications with the mix tool

The application behavior

Starting the application

Library applications

Implementing the application callback

The application folder structure

11.2 Working with dependencies

Adding a dependency

Adapting the pool

Visualizing the system

11.3 Building a web server

Choosing dependencies

Starting the server

Handling requests

Reasoning about the system

11.4 Configuring applications

Application environment

Varying configuration

Config script considerations

12 Building a distributed system

12.1 Distribution primitives

Starting a cluster

Communicating between nodes

Process discovery

Links and monitors

Other distribution services

12.2 Building a fault-tolerant cluster

Cluster design

The distributed to-do cache

Implementing a replicated database

Testing the system

Detecting partitions

Highly available systems

12.3 Network considerations

Node names

Cookies

Hidden nodes

Firewalls

13 Running the system

13.1 Running a system with Elixir tools

Using the mix and elixir commands

Running scripts

Compiling for production

13.2 OTP releases

Building a release

Using a release

Release contents

Packaging in a Docker container

13.3 Analyzing system behavior

Debugging

Logging

Interacting with the system

Tracing

index