Slide 1

Slide 1 text

@nirev Elixir: O que pode dar errado? 2019-05-25 Guilherme de Maio

Slide 2

Slide 2 text

@nirev @nirev

Slide 3

Slide 3 text

@nirev Baseado em São Paulo
 
 Elixir @ xerpa.com.br (2015)
 
 coorganizador SP Elixir Meetup ;)
 
 Muito Java e C antes

Slide 4

Slide 4 text

@nirev Elixir Engineer @ Telnyx
 Baseado em São Paulo
 
 Elixir @ xerpa.com.br (2015)
 
 coorganizador SP Elixir Meetup ;)
 
 Muito Java e C antes

Slide 5

Slide 5 text

@nirev

Slide 6

Slide 6 text

@nirev

Slide 7

Slide 7 text

@nirev

Slide 8

Slide 8 text

@nirev Contexto Coisas que normalmente associamos com a BEAM

Slide 9

Slide 9 text

@nirev Modelo de Atores

Slide 10

Slide 10 text

@nirev Processos Isolados

Slide 11

Slide 11 text

@nirev GC por Processo

Slide 12

Slide 12 text

@nirev Super escalável, só escreve, funciona!

Slide 13

Slide 13 text

@nirev Mas.. Essa é uma visão limitada

Slide 14

Slide 14 text

@nirev Como quebrar sua app

Slide 15

Slide 15 text

@nirev O Caso da tonelada de átomos 1

Slide 16

Slide 16 text

@nirev Tons of atoms Poison.decode(body, keys: :atoms!)

Slide 17

Slide 17 text

@nirev defmodule X do def explode do Enum.each(1 ..2_000_000, fn i -> IO.puts(" #{i}") 20 |> :crypto.strong_rand_bytes() |> Base.encode64() |> String.to_atom() end) end end Tons of atoms

Slide 18

Slide 18 text

@nirev 82534 ... 82535 ... 82536 ... 82537 ... 82538 ... 82539 ... 82540 ... no more index entries in atom_tab (max=100000) Crash dump is being written to: erl_crash.dump ...done Tons of atoms

Slide 19

Slide 19 text

@nirev • Nomes de Modulos • Nomes de nós • Campos de Struct • “decode as atom” @nirev Tons of atoms

Slide 20

Slide 20 text

@nirev O Caso do Agent linkado 2

Slide 21

Slide 21 text

@nirev Linked Agent Process 1 Linked Process start_link

Slide 22

Slide 22 text

@nirev Linked Agent Process 1 Linked Process start_link exit reason: exception

Slide 23

Slide 23 text

@nirev Linked Agent Process 1 Linked Process start_link exit reason: exception

Slide 24

Slide 24 text

@nirev Linked Agent Process 1 Linked Process start_link exit reason: normal

Slide 25

Slide 25 text

@nirev Linked Agent Linked Process “I'm a survivor”

Slide 26

Slide 26 text

@nirev Linked Agent defmodule X do def spawn do spawn(fn -> {:ok, _pid} = Agent.start_link(fn -> 42 end) Process.sleep(1_000) exit(:normal) end) end end

Slide 27

Slide 27 text

@nirev Linked Agent iex(1)> Process.list() |> Enum.count() 50 iex(2)> for _ <- 1 ..10, do: X.spawn [#PID<0.94.0>, #PID<0.95.0>, #PID<0.96.0>, #PID<0.97.0>, #PID<0.98.0>, #PID<0.99.0>, #PID<0.100.0>, #PID<0.101.0>, #PID<0.102.0>, #PID<0.103.0>] iex(3)> Process.list() |> Enum.count() 70 iex(4)> Process.sleep(2_000) :ok iex(5)> Process.list() |> Enum.count() 60

Slide 28

Slide 28 text

@nirev Linked Agent ref = Process.monitor(pid) receive do {:DOWN, ^ref, :process, ^pid, _reason} -> # receive down message and do something

Slide 29

Slide 29 text

@nirev

Slide 30

Slide 30 text

@nirev O Caso da Monitoração de Requests 3

Slide 31

Slide 31 text

@nirev Request Monitoring request Tracker pega dado do db api call atualizar status no db publicar na fila

Slide 32

Slide 32 text

@nirev Request Monitoring request Tracker pega dado do db api call atualizar status no db publicar na fila Tracker.add_breadcrumb(:key, metadata) Tracker.add_breadcrumb(:update_db, %{user: x, role: y, ..})

Slide 33

Slide 33 text

@nirev Request Monitoring request Tracker CRASH breadcrumbs = Tracker.get_breadcrumbs(tracker) send_report(exception, breadcrumbs)
 pega dado do db api call atualizar status no db publicar na fila

Slide 34

Slide 34 text

@nirev Request Monitoring Pra cada request: -Iniciar um agent -monitorar o agent e o processo -matar o agent quando o processo termina normalmente 
 OU 
 pegar os breadcrumbs e depois matar em caso de exception

Slide 35

Slide 35 text

@nirev Request Monitoring 40 80 120 160 ??????

Slide 36

Slide 36 text

@nirev Request Monitoring Cowboy implements the keep-alive mechanism by reusing the same process for all requests. This allows Cowboy to save memory. This works well because most code will not have any side effect impacting subsequent requests. But it also means you need to clean up if you do have code with side effects. The terminate/3 function can be used for this purpose.

Slide 37

Slide 37 text

@nirev Request Monitoring 40 80 120 160 agents forever

Slide 38

Slide 38 text

@nirev O Caso dos Restarts Infinitos 4

Slide 39

Slide 39 text

@nirev Infinite Restarts Process

Slide 40

Slide 40 text

@nirev Infinite Restarts Process

Slide 41

Slide 41 text

@nirev Infinite Restarts Error
 Reporter
 Task Process

Slide 42

Slide 42 text

@nirev Infinite Restarts Task Supervisor Error
 Reporter
 Task Error
 Reporter
 Task Process

Slide 43

Slide 43 text

@nirev Infinite Restarts Task
 Supervisor Error
 Reporter
 Task Error
 Reporter
 Task API remota caiu!!!

Slide 44

Slide 44 text

@nirev Infinite Restarts Task
 Supervisor supervisor(Task.Supervisor, [[name: XXX.TaskSupervisor, 
 restart: :transient]])

Slide 45

Slide 45 text

@nirev Infinite Restarts Task
 Supervisor Error
 Reporter
 Task Error
 Reporter
 Task

Slide 46

Slide 46 text

@nirev Infinite Restarts Task
 Supervisor Error
 Reporter
 Task Error
 Reporter
 Task API remota caiu!!!

Slide 47

Slide 47 text

@nirev Infinite Restarts Task
 Supervisor supervisor(Task.Supervisor, [[name: XXX.TaskSupervisor, 
 restart: :temporary]])

Slide 48

Slide 48 text

@nirev O Caso do Message Router 5

Slide 49

Slide 49 text

@nirev Message Router Dispositivo de rastreamento

Slide 50

Slide 50 text

@nirev Backend Server TCP Socket A P I Comandos Message Router

Slide 51

Slide 51 text

@nirev Message Router Message Router 1 Genserver x Veículo

Slide 52

Slide 52 text

@nirev Message Router Out of Memory Message Router

Slide 53

Slide 53 text

@nirev Message Router Process Control Block Stack Heap Process Memory
 Layout

Slide 54

Slide 54 text

@nirev Message Router Process Control Block Stack Heap
 
 Process Memory
 Layout Shared Heap Refc Binary ProcBin (pointers)

Slide 55

Slide 55 text

@nirev Message Router Process Control Block Stack Heap Private Heap Garbage Collection Generational
 • young generation: dados recentemente alocados • old generation: dados que sobrevivem uma
 passada de GC
 Fullsweep vs Generational:
 • min_heap_size • fullsweep_after

Slide 56

Slide 56 text

@nirev Message Router Shared Heap Garbage Collection Contagem de Referências
 • todo binary sem referencias a ele será coletado Shared Heap Refc Binary

Slide 57

Slide 57 text

@nirev Message Router O problema do Message Router Recebe mensagens > 64bytes (Refc bins) Então, uma referencia ao Large Binary fica no heap Message Router

Slide 58

Slide 58 text

@nirev Message Router O problema do Message Router Recebe mensagens > 64bytes (Refc bins) Então, uma referencia ao Large Binary fica no heap Mas, como o router não usa muita memória O heap não cresce mais que o min_heap_size Message Router

Slide 59

Slide 59 text

@nirev Message Router O problema do Message Router Recebe mensagens > 64bytes (Refc bins) Então, uma referencia ao Large Binary fica no heap Mas, como o router não usa muita memória O heap não cresce mais que o min_heap_size Logo, referencias vivem pra sempre, e os "large binaries" continuam no Shared Heap. Message Router

Slide 60

Slide 60 text

@nirev Message Router O problema do Message Router Recebe mensagens > 64bytes (Refc bins) Então, uma referencia ao Large Binary fica no heap Mas, como o router não usa muita memória O heap não cresce mais que o min_heap_size Logo, referencias vivem pra sempre, e os "large binaries" continuam no Shared Heap. Message Router Out of Memory

Slide 61

Slide 61 text

@nirev Message Router A SOLUÇÃO pro Message Router 1) fullsweep_after process flag
 configura a frequencia do fullsweep GC,
 e isso vai coletar os ProcBins
 2) Hibernar o processo
 quando hiberna, o fullsweep GC roda
 3) Mover o trabalho para processos de vida curta, 
 se possível Message Router

Slide 62

Slide 62 text

@nirev O que fazer quando isso acontece

Slide 63

Slide 63 text

@nirev O que fazer quando isso acontece (antes de acontecer)

Slide 64

Slide 64 text

@nirev Introspecção!

Slide 65

Slide 65 text

@nirev Conectar a um nó $ iex Erlang/OTP 21 [erts-10.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] Interactive Elixir (1.8.1) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> Node.list []

Slide 66

Slide 66 text

@nirev Conectar a um nó $ iex --sname node1@localhost --cookie delicia Erlang/OTP 21 [erts-10.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] Interactive Elixir (1.8.1) - press Ctrl+C to exit (type h() ENTER for help) iex(node1@localhost)1> Node.list []

Slide 67

Slide 67 text

@nirev Conectar a um nó $ iex --sname node2 --cookie delicia --remsh node1@localhost Erlang/OTP 21 [erts-10.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] Interactive Elixir (1.8.1) - press Ctrl+C to exit (type h() ENTER for help) iex(node1@localhost)1> Node.list [:node2@C02T24Z2HF1R]

Slide 68

Slide 68 text

@nirev Conectar a um nó $ iex --sname node2 --cookie delicia --remsh node1@localhost Erlang/OTP 21 [erts-10.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] Interactive Elixir (1.8.1) - press Ctrl+C to exit (type h() ENTER for help) iex(node1@localhost)1> Node.list [:node2@C02T24Z2HF1R]

Slide 69

Slide 69 text

@nirev E agora?

Slide 70

Slide 70 text

@nirev Veja o que está acontecendo! Process.list/0 Process.info/1 :sys.get_* MeuModulo.minha_task()

Slide 71

Slide 71 text

@nirev :observer.start() Veja o que está acontecendo!

Slide 72

Slide 72 text

@nirev :observer.start() Veja o que está acontecendo!

Slide 73

Slide 73 text

@nirev https: //ferd.github.io/recon/
 
 Recon is a library to be dropped into any other Erlang project, to be used to assist DevOps people diagnose problems in production nodes. Veja o que está acontecendo!

Slide 74

Slide 74 text

@nirev https: //ferd.github.io/recon/
 
 :recon.bin_leak(3) [ {#PID<0.80.0>, -606, [current_function: {Process, :sleep, 1}, initial_call: {:erlang, :apply, 2}]}, {#PID<0.124.0>, -176, [ :ssl_manager, {:current_function, {:gen_server, :loop, 7}}, {:initial_call, {:proc_lib, :init_p, 5}} ]}, {#PID<0.1905.0>, -165, [current_function: {:ranch_conns_sup, :loop, 4}, initial_call: {:proc_lib, :init_p, 5} ]} ] Veja o que está acontecendo!

Slide 75

Slide 75 text

@nirev Métricas!

Slide 76

Slide 76 text

@nirev VM metrics vmstats: manda metricas pro statsd
 https: //github.com/ferd/vmstats prometheus: manda métricas pro… prometheus https: //github.com/deadtrickster/prometheus.ex

Slide 77

Slide 77 text

@nirev

Slide 78

Slide 78 text

@nirev Visibilidade!

Slide 79

Slide 79 text

@nirev Log aggregation

Slide 80

Slide 80 text

@nirev Error Reporting -Sentry -Honeybadger -Bugsnag -Rollbar -…

Slide 81

Slide 81 text

@nirev Moral da história?

Slide 82

Slide 82 text

@nirev Tem várias maneiras de quebrar suas aplicações.

Slide 83

Slide 83 text

@nirev Não vá para produção 
 sem visibilidade.

Slide 84

Slide 84 text

@nirev Esteja preparado!

Slide 85

Slide 85 text

Guilherme de Maio
 [email protected] Obrigado! ❤ Elixir Brasil 2019 Check us out! 
 www.telnyx.com @nirev