$30 off During Our Annual Pro Sale. View Details »

Aprendizados de um projeto Elixir OTP

Aprendizados de um projeto Elixir OTP

Elixir Brasil 2019

Amanda

May 26, 2019
Tweet

More Decks by Amanda

Other Decks in Technology

Transcript

  1. Aprendizados de um
    projeto Elixir/OTP

    View Slide

  2. → amandasposito.com
    → speakerdeck.com/amandasposito
    → linkedin.com/in/amandasposito

    View Slide

  3. careers.plataformatec.com.br

    View Slide

  4. Disclaimer

    View Slide

  5. Como foi começar em
    um projeto Elixir, vindo
    de outra linguagem?

    View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. Como utilizar o
    ferramental da
    linguagem a seu favor?

    View Slide

  10. View Slide

  11. Lidar com banco de
    dados se tornou muito
    mais comum

    View Slide

  12. Preloads

    View Slide

  13. View Slide

  14. SELECT * FROM Courses;
    SELECT * FROM Users WHERE course_id = ?
    SELECT * FROM Users WHERE course_id = ?
    SELECT * FROM Users WHERE course_id = ?

    View Slide

  15. View Slide

  16. Repo.all from c in Course, preload: [:users]

    View Slide

  17. Ecto tem muitas
    features interessantes
    que podem te ajudar no
    dia-a-dia

    View Slide

  18. → Changesets

    View Slide

  19. → Changesets
    → Schemas

    View Slide

  20. → Changesets
    → Schemas
    → Ecto.Multi

    View Slide

  21. → Changesets
    → Schemas
    → Ecto.Multi
    → etc.

    View Slide

  22. Lidando com tabelas
    gigantescas

    View Slide

  23. Qualquer coisa que
    fossemos alterar no
    banco era difícil

    View Slide

  24. View Slide

  25. View Slide

  26. use Mix.Task

    View Slide

  27. defmodule Mix.Tasks.Echo do
    use Mix.Task
    @impl Mix.Task
    def run(_args) do
    Mix.shell.info "Hello World"
    end
    end

    View Slide

  28. Saber analisar a
    performance é muito
    importante

    View Slide

  29. Explain Analyze

    View Slide

  30. EXPLAIN ANALYZE
    SELECT
    "courses"."id",
    "courses"."data"
    FROM "courses"
    INNER JOIN "users" ON ("courses"."user_id" = "users"."id")
    WHERE "users".account_id = 4500000;

    View Slide

  31. Gather (cost=847239.21..2458486.98 rows=2 width=16) (actual time=35048.574..45161.127 rows=2 loops=1)
    Workers Planned: 2
    Workers Launched: 2
    -> Hash Join (cost=846239.21..2457486.78 rows=1 width=16) (actual time=32820.132..45130.374 rows=1 loops=3)
    Hash Cond: (courses.user_id = users.id)
    -> Parallel Seq Scan on courses (cost=0.00..1040678.17 rows=45193617 width=16) (actual time=0.044..29710.148 rows=33333333 loops=3)
    -> Hash (cost=846239.20..846239.20 rows=1 width=8) (actual time=8318.392..8318.392 rows=1 loops=3)
    Buckets: 1024 Batches: 1 Memory Usage: 9kB
    -> Seq Scan on users (cost=0.00..846239.20 rows=1 width=8) (actual time=819.773..8318.380 rows=1 loops=3)
    Filter: (account_id = 4500000)
    Rows Removed by Filter: 49999999
    Planning time: 0.421 ms
    Execution time: 45169.333 ms

    View Slide

  32. View Slide

  33. View Slide

  34. http://blog.plataformatec.com.br/2019/02/migrations-in-databases-with-large-amount-of-data/

    View Slide

  35. Tabelas ETS

    View Slide

  36. "These provide the ability to store
    very large quantities of data in an
    Erlang runtime system, and to have
    constant access time to the data."
    — Erlang Documentation

    View Slide

  37. def hello do
    opts = [
    :set,
    :named_table,
    :public,
    read_concurrency: true
    ]
    table_name = :elixir_brasil
    :ets.new(table_name, opts)
    for n <- 1..100 do
    :ets.insert(table_name, {n, "Key #{n}"})
    end
    end

    View Slide

  38. View Slide

  39. Nesse momento não
    tinhamos muita visão
    da quantidade de
    dados

    View Slide

  40. Velocidade
    x
    Quantidade de itens

    View Slide

  41. A estimativa estava em
    76GB de consumo de
    memória por máquina

    View Slide

  42. View Slide

  43. Tabelas ETS não
    possuem muitas
    opções de otimização
    como o Redis

    View Slide

  44. View Slide

  45. São como se fossem um
    Hash direto na memória

    View Slide

  46. Consomem a memória
    disponível da aplicação

    View Slide

  47. View Slide

  48. https://moz.com/devblog/moz-analytics-db-free

    View Slide

  49. View Slide

  50. Testes

    View Slide

  51. Doctest != Test

    View Slide

  52. View Slide

  53. Sua aplicação está se
    comunicando com o
    mundo exterior, e
    agora?

    View Slide

  54. View Slide

  55. View Slide

  56. View Slide

  57. View Slide

  58. Como fazer para
    stubar/mockar uma
    requisição?

    View Slide

  59. Bypass

    View Slide

  60. View Slide

  61. setup do
    bypass = Bypass.open
    {:ok, bypass: bypass}
    end

    View Slide

  62. test "fetch/1 returns and formats tweets", %{bypass: bypass} do
    response = Jason.encode!([%{"text" => "Elixir Brasil 2019"}])
    Bypass.expect(bypass, fn conn ->
    assert "/1.1/search/tweets.json" == conn.request_path
    assert "GET" == conn.method
    Plug.Conn.resp(conn, 200, response)
    end)
    tweets = TwitterClient.fetch("http://localhost:#{bypass.port}")
    assert tweets == [%{"text" => "Elixir Brasil 2019"}]
    end

    View Slide

  63. test "fetch/1 returns and formats tweets", %{bypass: bypass} do
    response = Jason.encode!([%{"text" => "Elixir Brasil 2019"}])
    Bypass.expect(bypass, fn conn ->
    assert "/1.1/search/tweets.json" == conn.request_path
    assert "GET" == conn.method
    Plug.Conn.resp(conn, 200, response)
    end)
    tweets = TwitterClient.fetch("http://localhost:#{bypass.port}")
    assert tweets == [%{"text" => "Elixir Brasil 2019"}]
    end

    View Slide

  64. def fetch(url \\ "https://api.twitter.com") do
    {:ok, response} = HTTPoison.get("#{url}/1.1/search/tweets.json")
    Jason.decode!(response.body)
    end

    View Slide

  65. Quando usar bypass?

    View Slide

  66. Código que precisa
    realizar requests HTTP

    View Slide

  67. View Slide

  68. Mox

    View Slide

  69. View Slide

  70. View Slide

  71. test "messages/0 lists all messages from the timeline" do
    TwitterMock
    |> expect(:fetch, fn -> [%{"text" => "Olá mundo"}] end)
    assert Timeline.messages() == {:ok, 1}
    end
    https://github.com/amandasposito/mox_example

    View Slide

  72. http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/

    View Slide

  73. GenServer

    View Slide

  74. Como testar
    processos?

    View Slide

  75. Testar Callback de
    GenServer não é uma
    boa prática

    View Slide

  76. View Slide

  77. GenServer.cast

    View Slide

  78. GenServer.call

    View Slide

  79. https://devonestes.herokuapp.com/unit-tests-in-elixir-part-2

    View Slide

  80. Quais os problemas
    mais comuns
    encontrados?

    View Slide

  81. → Tudo que eu aprendi em OOP, vou jogar fora em
    funcional?
    → Como eu organizo meu código?
    → Todos os problemas que eu tinha em OOP, somem
    em funcional?
    → E esse tal de Context? Comé qui usa?

    View Slide

  82. A probabilidade do
    código ficar complexo
    diminui

    View Slide

  83. Mas os problemas
    ainda existem

    View Slide

  84. Muitos dos problemas
    que vemos em OOP
    reconhecemos em FP

    View Slide

  85. → Funções muito grandes

    View Slide

  86. → Funções muito grandes
    → Funções difíceis de testar

    View Slide

  87. → Funções muito grandes
    → Funções difíceis de testar
    → Mudanças simples precisam ser feitas em vários
    lugares

    View Slide

  88. → Funções muito grandes
    → Funções difíceis de testar
    → Mudanças simples precisam ser feitas em vários
    lugares
    → Feature Envy

    View Slide

  89. → Funções muito grandes
    → Funções difíceis de testar
    → Mudanças simples precisam ser feitas em vários
    lugares
    → Feature Envy
    → Contextos com muitas linhas

    View Slide

  90. → Funções muito grandes
    → Funções difíceis de testar
    → Mudanças simples precisam ser feitas em vários
    lugares
    → Feature Envy
    → Contextos com muitas linhas
    → Acoplamento

    View Slide

  91. → Funções muito grandes
    → Funções difíceis de testar
    → Mudanças simples precisam ser feitas em vários
    lugares
    → Feature Envy
    → Contextos com muitas linhas
    → Acoplamento

    View Slide

  92. https://youtu.be/eldYot7uxUc

    View Slide

  93. View Slide

  94. Contexts

    View Slide

  95. A idéia é definir limites
    entre diferentes
    módulos da aplicação

    View Slide

  96. Com o passar do tempo
    e a necessidade de
    interação com vários
    schemas

    View Slide

  97. Os contextos podem
    ficar maiores do que
    deveriam

    View Slide

  98. Preste atenção para
    não manter código
    ortogonal ao contexto,
    no contexto

    View Slide

  99. Mantenha queries
    próximas de seu
    schema

    View Slide

  100. A exceção são queries
    que lidam com mais de
    um schema

    View Slide

  101. http://devonestes.herokuapp.com/a-proposal-for-context-rules

    View Slide

  102. View Slide

  103. Umbrella Projects

    View Slide

  104. Dentro de um Umbrella
    app os apps deveriam
    acessar arquivos uns
    dos outros?

    View Slide

  105. É uma maneira de
    organizar seu código

    View Slide

  106. View Slide

  107. View Slide