Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
September 8, 2022 07:17 pm GMT

Como automatizamos a avaliao de projetos com Github Actions e o Broadway do Elixir

Introduo

Aqui na Trybe, ao final de cada seo de contedos, as pessoas estudantes precisam desenvolver um projeto para avaliarmos seu aprendizado. O repositrio do projeto disponibilizado no Github e elas fazem clone para iniciar o desenvolvimento em suas mquinas. A cada commit da pessoa no repositrio ns disparamos a execuo de uma avaliao atravs das Github Actions.

Processamento de uma delivery (commit)

Aps abrir um pull request com o cdigo desenvolvido, cada commit dispara uma github action que ir avaliar o cdigo da pessoa estudante e guardar no nosso banco de dados informaes sobre a avaliao do cdigo dessa pessoa.

Os commits so enfileirados e assim conseguimos controlar o status do seu processamento, para garantir que todos os commits tenham suas avaliaes devidamente calculadas. Em caso de falhas, reprocessamos automaticamente a cada 5 minutos.

Fluxo de processamento da delivery

O processamento do commit no backend foi implementado em cima do conceito de processamento de dados em multi-etapas com a biblioteca Broadway. Consistindo em um mdulo produtor que responsvel por buscar no banco todas as deliveries que esto aguardando processamento para repassar ao consumidor. J o consumidor o mdulo Broadway que responsvel por demandar as avaliaes ao produtor, processar a nota e criar o comentrio de feedback no Github.

Como temos uma grande quantidade de commits sendo feitos nos projetos, a primeira coisa que tentamos fazer utilizar os recursos da programao concorrente disponvel na linguagem Elixir para processar vrias deliveries ao mesmo tempo, com o objetivo de evitar um atraso no feedback para as pessoas estudantes.

No cdigo abaixo, observe nas configuraes do Broadway que na linha 10 foi definido qual o mdulo produtor e nas linhas 16 e 17 foi definido a quantidade de processos concorrentes e a quantidade de demanda que cada processo ir solicitar ao produtor.

defmodule Trybe.Consumer do  use Broadway  alias Broadway.Message  def start_link(_opts) do    Broadway.start_link(__MODULE__,      name: __MODULE__,      producer: [        module: Trybe.Producer,        transformer: {__MODULE__, :transform, []},        concurrency: 1      ],      processors: [        default: [          concurrency: 10,          max_demand: 10        ]      ]    )  end  def handle_message(_processor, message, _context),    do: Message.update_data(message, &process_data/1)  def transform(event, _opts),    do: %Message{data: event, acknowledger: {__MODULE__, :ack_id, :ack_data}}  def ack(:ack_id, _successful, _failed), do: :ok  defp process_data(delivery) do    # => 1- Calcula a nota e salva no banco    # => 2- Faz request no Github criando o comentrio de feedback.    # => 3- Atualiza o status da avaliao para `finished`.  endend

J no produtor, com base na demanda definida nas configuraes do Broadway, seu papel buscar no banco de dados as deliveries que esto aguardando o processamento e repassar para o consumidor. Caso o produtor no encontre mais deliveries, feito um agendamento de um novo processo para buscar novas deliveries nos prximos 10 segundos.

defmodule Trybe.Producer do  use GenStage  alias Trybe.Deliveries  def start_link(initial \\ []) do    GenStage.start_link(__MODULE__, initial, name: __MODULE__)  end  def init(deliveries) do    {:producer, deliveries}  end  # Funo que ir enviar as deliveries para o consumidor  def handle_demand(demand, state) when demand > 0 do    send_deliveries(demand, state)  end  # Funo que  chamada quando um processo  agendado   # solicitando mais deliveries.  def handle_info({:get_deliveries, demand}, state) do     send_deliveries(demand, state)  end  defp send_deliveries(demand, state) do    deliveries = list_deliveries(demand)    maybe_schedule_next_deliveries(deliveries, demand)    {:noreply, deliveries, state}  end  # Funo responsvel por buscar novas deliveries   # nos prximos 10 segundos caso a quantidade de deliveries   # no estado seja menor do que o solicitado.  defp maybe_schedule_next_deliveries(deliveries, demand) when length(deliveries) == demand,    do: nil  defp maybe_schedule_next_deliveries(_deliveries, demand) do    Process.send_after(self(), {:get_deliveries, demand}, :timer.seconds(10))  end  # Funo responsvel por buscar as deliveries no banco   # e mudar o status delas para `processing`.  defp list_deliveries(demand) do    with deliveries when deliveries != [] <- Deliveries.list_deliveries_waiting_process(demand),         {_, deliveries} <- Deliveries.set_processing_status_to_deliveries(deliveries) do      deliveries    end  endend

Logo abaixo um exemplo de como o Broadway se comporta com as configuraes definidas.

Fluxo de processamento da nota no backend

Problemas ao fazer requests simultneas na API do Github

No primeiro momento tudo parecia perfeito, mas infelizmente no funcionou da maneira esperada por causa do rate limit do Github, que acabou gerando um grande atraso na entrega dos feedbacks para as pessoas estudantes.

Na API do Github existem dois tipos de rate limit para garantir a disponibilidade da API para todos. Aqui no post iremos focar apenas no secondary rate limit que nosso principal vilo, mas saiba que tambm existe o primary rate limit.

Na documentao do Github existem algumas prticas recomendadas para evitar o rate limit secundrio, e os nossos maiores viles so:

  • No faa solicitaes simultneas para um mesmo token.
  • Solicitaes que criam contedo que acionam notificaes, tais como issues, comentrios e pull requests, podem ser ainda mais limitadas.Crie este contedo em um ritmo razovel para evitar maiores limitaes.

Requisies moderadas ao Github

Sorte a nossa que o Broadway j estava preparado para esse tipo de problema e implementou uma feature que nos possibilita botar o p no freio do nosso produtor. A configurao a rate_limiting, com ela possvel definir a quantidade de deliveries que podemos processar em um intervalo de tempo.

defmodule Trybe.Consumer do  use Broadway  ......  ......  def start_link(_opts) do    Broadway.start_link(__MODULE__,      name: __MODULE__,      producer: [        module: Trybe.Producer,        transformer: {__MODULE__, :transform, []},        concurrency: 1,        rate_limiting: [          allowed_messages: 10,          interval: 30_000        ]      ],      processors: [        default: [          concurrency: 1,          max_demand: 10        ]      ]    )  endend

Mas s essa configurao ainda no foi o suficiente, ela apenas desacelera a quantidade de requisies que fazemos no servio em um intervalo de tempo, mas o problema de requests simultneas ainda permanece. Dito isso, tivemos que tambm mudar as configuraes dos processors, definindo o campo concurrency: 1 para que exista apenas um nico processo recebendo deliveries do produtor evitando requisies concorrentes.

Fluxo de processamento da nota evitando rate limit

Dessa forma, conseguimos fazer requisies moderadas e sequenciais no servio do Github sem comprometer o tempo de retorno do feedback para as pessoas estudantes.

Concluso

A soluo implementada nos possibilitou controlar o secondary rate limit e assim conseguimos processar os feedbacks para as pessoas estudantes de forma eficiente, ou seja, com poucas linhas de cdigo podemos reajustar as configuraes do Broadway novamente para que o processamento de deliveries possa ser feita de maneira concorrente em grande escala.

Se voc tiver curiosidade de saber como a infraestrutura que permite a Trybe corrigir mais de 20mil avaliaes por semana, recomendo tambm a leitura do post publicado pelo Biasi.


Original Link: https://dev.to/trybe/como-automatizamos-a-avaliacao-de-projetos-com-github-actions-e-o-broadway-do-elixir-2pjf

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To