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 Slide

  2. @nirev
    @nirev

    View Slide

  3. @nirev
    Baseado em São Paulo


    Elixir @ xerpa.com.br (2015)


    coorganizador SP Elixir Meetup ;)


    Muito Java e C antes

    View 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 Slide

  5. @nirev

    View Slide

  6. @nirev

    View Slide

  7. @nirev

    View Slide

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

    View Slide

  9. @nirev
    Modelo de Atores

    View Slide

  10. @nirev
    Processos Isolados

    View Slide

  11. @nirev
    GC por Processo

    View Slide

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

    View Slide

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

    View Slide

  14. @nirev
    Como quebrar sua app

    View Slide

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

    View Slide

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

    View Slide

  17. @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 Slide

  18. @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 Slide

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

    View Slide

  20. @nirev
    O Caso do
    Agent linkado
    2

    View Slide

  21. @nirev
    Linked Agent
    Process 1 Linked
    Process
    start_link

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  26. @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 Slide

  27. @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 Slide

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

    View Slide

  29. @nirev

    View Slide

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

    View Slide

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

    View Slide

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

  33. @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 Slide

  34. @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 Slide

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

    View Slide

  36. @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 Slide

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

    View Slide

  38. @nirev
    O Caso dos
    Restarts Infinitos
    4

    View Slide

  39. @nirev
    Infinite Restarts
    Process

    View Slide

  40. @nirev
    Infinite Restarts
    Process

    View Slide

  41. @nirev
    Infinite Restarts
    Error

    Reporter

    Task
    Process

    View Slide

  42. @nirev
    Infinite Restarts
    Task
    Supervisor
    Error

    Reporter

    Task
    Error

    Reporter

    Task
    Process

    View Slide

  43. @nirev
    Infinite Restarts
    Task

    Supervisor
    Error

    Reporter

    Task
    Error

    Reporter

    Task
    API remota caiu!!!

    View Slide

  44. @nirev
    Infinite Restarts
    Task

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

    restart: :transient]])

    View Slide

  45. @nirev
    Infinite Restarts
    Task

    Supervisor
    Error

    Reporter

    Task
    Error

    Reporter

    Task

    View Slide

  46. @nirev
    Infinite Restarts
    Task

    Supervisor
    Error

    Reporter

    Task
    Error

    Reporter

    Task
    API remota caiu!!!

    View Slide

  47. @nirev
    Infinite Restarts
    Task

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

    restart: :temporary]])

    View Slide

  48. @nirev
    O Caso do
    Message Router
    5

    View Slide

  49. @nirev
    Message Router
    Dispositivo de rastreamento

    View Slide

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

    View Slide

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

    View Slide

  52. @nirev
    Message
    Router
    Out of Memory
    Message Router

    View Slide

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

    Layout

    View Slide

  54. @nirev
    Message Router
    Process Control Block
    Stack
    Heap


    Process Memory

    Layout
    Shared Heap
    Refc Binary
    ProcBin (pointers)

    View Slide

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

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

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

    View Slide

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

  58. @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 Slide

  59. @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 Slide

  60. @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 Slide

  61. @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 Slide

  62. @nirev
    O que fazer quando isso
    acontece

    View Slide

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

    View Slide

  64. @nirev
    Introspecção!

    View Slide

  65. @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 Slide

  66. @nirev
    Conectar a um nó
    $ iex --sname [email protected] --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([email protected])1> Node.list
    []

    View Slide

  67. @nirev
    Conectar a um nó
    $ iex --sname node2 --cookie delicia --remsh [email protected]
    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([email protected])1> Node.list
    [:[email protected]]

    View Slide

  68. @nirev
    Conectar a um nó
    $ iex --sname node2 --cookie delicia --remsh [email protected]
    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([email protected])1> Node.list
    [:[email protected]]

    View Slide

  69. @nirev
    E agora?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  73. @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 Slide

  74. @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 Slide

  75. @nirev
    Métricas!

    View Slide

  76. @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 Slide

  77. @nirev

    View Slide

  78. @nirev
    Visibilidade!

    View Slide

  79. @nirev
    Log aggregation

    View Slide

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

    View Slide

  81. @nirev
    Moral da história?

    View Slide

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

    View Slide

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

    sem visibilidade.

    View Slide

  84. @nirev
    Esteja preparado!

    View Slide

  85. Guilherme de Maio

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

    www.telnyx.com
    @nirev

    View Slide