Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Elixir: o que pode dar errado?

Elixir: o que pode dar errado?

Apresentação feita na https://2019.elixirbrasil.com :)

Normalmente fala-se muito de como uma linguagem nova é incrível e todas as suas possibilidades! Todos queremos mostrar casos de sucesso e que mais pessoas usem a linguagem que amamos. Porém, coisas dão errado! Nesta palestra vou mostrar alguns casos que podem quebrar sua aplicação Elixir em produção e como evitá-los.

More Decks by Guilherme de Maio, nirev

Other Decks in Programming

Transcript

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

    View full-size slide

  2. @nirev
    @nirev

    View full-size slide

  3. @nirev
    Baseado em São Paulo


    Elixir @ xerpa.com.br (2015)


    coorganizador SP Elixir Meetup ;)


    Muito Java e C antes

    View full-size slide

  4. @nirev
    Elixir Engineer @ Telnyx

    Baseado em São Paulo


    Elixir @ xerpa.com.br (2015)


    coorganizador SP Elixir Meetup ;)


    Muito Java e C antes

    View full-size slide

  5. @nirev
    Contexto
    Coisas que normalmente associamos com a BEAM

    View full-size slide

  6. @nirev
    Modelo de Atores

    View full-size slide

  7. @nirev
    Processos Isolados

    View full-size slide

  8. @nirev
    GC por Processo

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  11. @nirev
    Como quebrar sua app

    View full-size slide

  12. @nirev
    O Caso da
    tonelada de átomos
    1

    View full-size slide

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

    View full-size slide

  14. @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

    View full-size slide

  15. @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

    View full-size slide

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

    View full-size slide

  17. @nirev
    O Caso do
    Agent linkado
    2

    View full-size slide

  18. @nirev
    Linked Agent
    Process 1 Linked
    Process
    start_link

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  23. @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

    View full-size slide

  24. @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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  28. @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, ..})

    View full-size slide

  29. @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

    View full-size slide

  30. @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

    View full-size slide

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

    View full-size slide

  32. @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.

    View full-size slide

  33. @nirev
    Request Monitoring
    40
    80
    120
    160
    agents forever

    View full-size slide

  34. @nirev
    O Caso dos
    Restarts Infinitos
    4

    View full-size slide

  35. @nirev
    Infinite Restarts
    Process

    View full-size slide

  36. @nirev
    Infinite Restarts
    Process

    View full-size slide

  37. @nirev
    Infinite Restarts
    Error

    Reporter

    Task
    Process

    View full-size slide

  38. @nirev
    Infinite Restarts
    Task
    Supervisor
    Error

    Reporter

    Task
    Error

    Reporter

    Task
    Process

    View full-size slide

  39. @nirev
    Infinite Restarts
    Task

    Supervisor
    Error

    Reporter

    Task
    Error

    Reporter

    Task
    API remota caiu!!!

    View full-size slide

  40. @nirev
    Infinite Restarts
    Task

    Supervisor
    supervisor(Task.Supervisor, [[name: XXX.TaskSupervisor, 

    restart: :transient]])

    View full-size slide

  41. @nirev
    Infinite Restarts
    Task

    Supervisor
    Error

    Reporter

    Task
    Error

    Reporter

    Task

    View full-size slide

  42. @nirev
    Infinite Restarts
    Task

    Supervisor
    Error

    Reporter

    Task
    Error

    Reporter

    Task
    API remota caiu!!!

    View full-size slide

  43. @nirev
    Infinite Restarts
    Task

    Supervisor
    supervisor(Task.Supervisor, [[name: XXX.TaskSupervisor, 

    restart: :temporary]])

    View full-size slide

  44. @nirev
    O Caso do
    Message Router
    5

    View full-size slide

  45. @nirev
    Message Router
    Dispositivo de rastreamento

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  48. @nirev
    Message
    Router
    Out of Memory
    Message Router

    View full-size slide

  49. @nirev
    Message Router
    Process Control Block
    Stack
    Heap
    Process Memory

    Layout

    View full-size slide

  50. @nirev
    Message Router
    Process Control Block
    Stack
    Heap


    Process Memory

    Layout
    Shared Heap
    Refc Binary
    ProcBin (pointers)

    View full-size slide

  51. @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

    View full-size slide

  52. @nirev
    Message Router
    Shared Heap Garbage Collection
    Contagem de Referências

    • todo binary sem referencias a ele será coletado
    Shared Heap
    Refc Binary

    View full-size slide

  53. @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

    View full-size slide

  54. @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

    View full-size slide

  55. @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

    View full-size slide

  56. @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

    View full-size slide

  57. @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

    View full-size slide

  58. @nirev
    O que fazer quando isso
    acontece

    View full-size slide

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

    View full-size slide

  60. @nirev
    Introspecção!

    View full-size slide

  61. @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
    []

    View full-size slide

  62. @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
    []

    View full-size slide

  63. @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]

    View full-size slide

  64. @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]

    View full-size slide

  65. @nirev
    E agora?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  69. @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!

    View full-size slide

  70. @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!

    View full-size slide

  71. @nirev
    Métricas!

    View full-size slide

  72. @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

    View full-size slide

  73. @nirev
    Visibilidade!

    View full-size slide

  74. @nirev
    Log aggregation

    View full-size slide

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

    View full-size slide

  76. @nirev
    Moral da história?

    View full-size slide

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

    View full-size slide

  78. @nirev
    Não vá para produção 

    sem visibilidade.

    View full-size slide

  79. @nirev
    Esteja preparado!

    View full-size slide

  80. Guilherme de Maio

    [email protected]
    Obrigado!
    ❤ Elixir Brasil 2019
    Check us out! 

    www.telnyx.com
    @nirev

    View full-size slide