Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Rust, Go, Rinha e I/O

Posted on Sep 8 Este artigo é o início de um formato diferente de conteúdo que quero experimentar, um apanhado (ou resumão, ou dump, como queiram chamar) de coisas que tenho visto nos últimos dias, com uma pegada informal e um leve toque de didática como de costume.Em 2022 eu estava aprendendo Rust.Numa bela 4a feira de chuva, fiz um tweet dizendo que iria tentar implementar uma lista duplamente ligada em Rust. Minutos depois, fiz outro tweet dizendo que iria desistir e ver Netflix.Então desisti do Rust.Recentemente em 2023 comecei uma saga para aprender Golang. Como a sintaxe me incomodou um pouco, talvez pela verbosidade dos if err != nil a dar com telha, decidi continuar kkkMas adicionando uma complexidade nisto tudo: voltar a aprender Rust junto com Go. As vezes eu meto dessas, em 2015/16 decidi aprender Elixir e redes neurais artificiais tudo junto, e saiu o morphineNesta segunda tentativa com Rust, bateu um sentimento, confesso. Sentimento bom, no caso.Com isto, passei a praticar em Rust e Go (e porque não Ruby) estruturas de dados simples que já estou habituado a implementar (pq pratico muito): filas, pilhas, listas ligadas, mutexes etc etc. E claro, programação em sockets como de praxe.Desta massarocada toda saiu um UNIX server em Go, outro em Rust, pelo que indo de milho em milho, saiu um background job em ambas distintas linguagens.Cheguei a um background job em Rust muito overkill, vale a pena dar uma olhada, lá abordo lista duplamente ligada, fila duplamente terminada, rpoplpush, blocking queue, DLQ, retries, threads etc.Tudo isso implementado com os smart pointers em Rust, para resolver problemas inerentes (ok, não sem bem problemas) a ownership e borrow checker.Em breve vou escrever sobre meu aprendizado em Rust com artigos mais detalhados, preciso arrumar um tempo na minha vida, salvar pinguins na Antártida é mais urgente que issoMas se você acha que é ̶m̶u̶i̶t̶o̶ ̶m̶a̶i̶s̶ ̶e̶l̶e̶g̶a̶n̶t̶e̶ mais fácil ver um código Ruby, aqui neste arquivo no meu projeto leandronsp/fun também abordo um background job em Ruby com os mesmos conceitos aplicados ali em Rust e Go.Entre Julho e Agosto aconteceu na famosa #bolhaDev no Twitter uma competição chamada rinha de backend, criada pelo Zan do Twitter e nosso arauto do sarcasmo Will Correa. A ideia era que as pessoas participantes trouxessem a implementação de uma API definida nas regras da competição.O desafio tinha como requisito uma arquitetura composta por basicamente um NGINX fazendo load balancing para 2 API's mandando dados para um banco de dados, tudo rodando em containers.Se você quer saber mais sobre containers e Docker, dê uma olhadinha nesta série de artigos que escrevi centuries agoPra deixar mais desafiador ainda, havia uma restrição obrigatória de recursos onde o total de containers não podia exceder o limite de 1.5 CPU's e 3GB de memória. Em uma data previamente estipulada, todas as submissões seriam submetidas a test de stress (violento, diga-se de passagem) através de uma ferramenta chamada Gatling.Foi uma iniciativa muito bacana pois trouxe à luz pessoas que pouco interagiam por ali e que eram muito talentosas. Deu pra aprender e trocar muita figurinha durante os dias pré-submissão.Outra coisa boa foi que o polvo lá do Twitter organizou um encontro presencial que iria transmitir a live da rinha, onde deu pra conhecer mais pessoas interessadas no tema.Decidi participar submetendo uma versão em Ruby (sem Rails) tentando colocar em prova um web framework muito simples que criei chamado Chespirito.Infelizmente, durante os testes no meu ambiente local não consegui grandes números com o Chespirito e acabei indo de Roda, que por acaso é o pior framework que já vi, pois sou muito hater de DSL's em situações onde não precisamos delas.Um exemplo da tamanha verbosidade que atingimos com este framework:Escolhi Roda pq me disseram que era rápido. Não comparei com Sinatra, apenas confieiSubmeti então minha versão naquela terça-feira sombria às 22h42 com Roda e Puma, pois meu ambiente não me ajudou a tirar bons números com I/O assíncrono no Falcon.Logo mais chegamos no ponto do meu ambiente Portanto, minha submissão ficou assim:No docker-compose submetido, dividi os recursos da seguinte forma:Depois vou explicar como que distribuindo melhor os recursos e diminuindo os números do NGINX e PostgreSQL fez meu troughput melhorar, mas só consegui fazer isto dias depois da rinha ter terminado.Minha submissão Ruby ficou em 20º lugar num ranking de 51 submissões funcionais (quase 100 foram submetidas mas muitas não rodaram lá na máquina dos caras), com um total de 24k inserts no banco de dados após o teste de stress com carga dobrada.Não achei um número ruim mas eu tinha uma noção de que o desafio era muito I/O-bound, coisa que expliquei nesta thread do Twitter e também nesta outra thread dias depois.Se I/O-bound ou CPU-bound são termos esquisitos pra você, sugiro dar um passo atrás e aprender os fundamentos de concorrência em sistemas operacionais, neste super guia que escrevi anos atrás sobre o funcionamento da WebO resumo disto é que depois com mais calma, consegui ajustar melhor meu ambiente de desenvolvimento. Abandonei o colima que tava com performance horrível no meu macOS e abracei o orbstack. So far, so good.Com isto, pude rodar de forma mais assertiva com restrição de recursos os testes de stress no meu ambiente. Tem a ver com a virtualização do orbstack ser mais performática etc e tal.Isto abriu portas para que eu voltasse a experimentar Falcon e meu filho Chespirito. Guess what, os números começaram a bater a famigerada dupla Roda/Puma.Aproveitei também para utilizar o Portainer para visualizar métricas de CPU e memória dos containers no Docker.Não apenas isto, também mexi nos meus limites no docker-compose, ficando assim, delegando mais recursos para o PostgreSQL que, tadinho, era o que mais apanhava (parabéns guerreiro, tmj):Diferente de muitas submissões na rinha, fiquei entre poucos que optaram por não utilizar qualquer estratégia de cache ou batch insert de forma assíncrona. Meu intuito sempre foi experimentar algo que acredito muito e que trago nos projetos em que trabalho: não abusar de cache ou estratégia assíncrona onde não precisa. Cache ajuda mas pode trazer muitos desafios e encarecer custos no fim das contas.Claro que, para a rinha, tudo era válido. Foi um amontoado positivo de diferentes soluções e troca de conhecimentoMas como sou chato com custos, eu sempre vou pra solução mais simples possível e que causa menos entropia possível, até que se prove o contrário.Cenário então fica assim:Os números melhoraram, indo pra 35k. Not bad. Mas eu tinha uma leve suspeita de que algo errado ainda estava com minha solução. Por ser um desafio muito I/O-heavy, os requests ficavam pouco no Ruby, então eu tinha que melhorar a latência do PostgreSQL.Foi aí que inverti a lógica. Nesta thread compartilhei recentemente como consegui atingir 46k inserts, mas em suma eu basicamente percebi que muitos requests à espera (Gatling judia) fazem aumentar a latência e consequentemente ciclo de CPU para fazer gestão das filas nos sockets. Minha ideia então foi não deixar muito requests à espera, mesmo porque as queries no db são muito rápidas (remember kids, índices corretos salvam vidas).Para atingir isto, resolvi diminuir o PostgreSQL para 30 max_connections e NGINX para 256 worker_connections (podia até ser 128 ou 64 tbh). Na API, como são duas, deixei uma pool de 15 conexões, pois o PostgreSQL neste caso iria até 30.O resultado trouxe um troughput melhor e garantiu 46k inserts. Em breve vou escrever um artigo mais detalhado sobre esta saga do Ruby na rinhaIt turns out que também fiz outra versão e submeti, escrita em Bash script, apenas for fun mesmo. Foi produto de um tweet inocente que fiz, o pessoal não perdoou e fez o tweet viralizar, pelo que me senti obrigado e implementar a API em Bash.Na verdade, como eu já venho de uma saga ensinando fundamentos de computação nos meus artigos usando Bash, foi tranquilo fazer uma versão minimamente aceitável com mkfifo e netcat.Submeti sem grandes pretensões, rodei o teste local apenas uma vez e deu um monte de erro, pensei "freak it vou mandar mesmo assim" e foi.Para meu deleite, a solução em Bash ficou dentre as 51 funcionais da rinha, conquistando a tão sonhada 51ª posição, com um total de 17 inserts.Isso mesmo, 17 insertsTwitter não perdoa e então começaram a me chamar de "carinha do Bash", "ministro dos scripts Bash" e etc, mas gente EU NAO PROGRAMO EM BASH. Sou apenas um dev scriptzero que usa Bash as vezes pra facilitar minha vida e automatizar o que não preciso, mas longe de "manjar" de Bash kkkkkCom o término da rinha, foi gerado um buzz muito alto em torno de I/O, principalmente o famoso I/O assíncrono, ou então como alguns costumam referenciar por I/O não-bloqueante.Em breve escrevo artigos mais formais e detalhados sobre I/O não-bloqueante, mas recentemente fui de live mesmo, com um formato mais de "explicação", indo lá atrás de forma resumida nos aspectos de concorrência em sistemas operacionais até chegar em I/O não-bloqueante. Na live eu trouxe exemplos em Ruby, Bash e C. Se você quer dar uma espreitada no que aconteceu por lá, CLIQUE AQUI.É isto, o intuito deste breve artigo foi fazer um apanhado das coisas que tenho olhado recentemente, em um formato mais informal como costumo usar no Twitter. Espero que o formato possa ser útil, caso contrário irei apenas continuar com aquele formato ̶c̶h̶a̶t̶o̶ denso e didático.Claro que não deixarei de escrever os artigos técnicos de costume. E pode ser que eu misture inglês com português e a massarocada toda.Fiquem ligades.Templates let you quickly answer FAQs or store snippets for re-use.Curti o formato, como ideia seria legal trazer um artigo detalhando cada um dos tópicos (um artigo falando da treta que direto aparece "migrei minha API de Rust pra Go em 15 dias" "migrei minha API de Go pra Rust e ganhei 3 bilhões de performance", principalmente na gringa), um sobre as APIs da Rinha etc. Muito daora Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well Confirm For further actions, you may consider blocking this person and/or reporting abuse Omar Hiari - Jul 13 Matheus dos Reis de Jesus - Jul 13 Ayoub Ali - Jul 13 Povilas Jurčys - Jul 13 Once suspended, leandronsp will not be able to comment or publish posts until their suspension is removed. Once unsuspended, leandronsp will be able to comment and publish posts again. Once unpublished, all posts by leandronsp will become hidden and only accessible to themselves. If leandronsp is not suspended, they can still re-publish their posts from their dashboard. Note: Once unpublished, this post will become invisible to the public and only accessible to Leandro Proença. They can still re-publish the post if they are not suspended. Thanks for keeping DEV Community safe. Here is what you can do to flag leandronsp: leandronsp consistently posts content that violates DEV Community's code of conduct because it is harassing, offensive or spammy. Unflagging leandronsp will restore default visibility to their posts. DEV Community — A constructive and inclusive social network for software developers. With you every step of your journey. Built on Forem — the open source software that powers DEV and other inclusive communities.Made with love and Ruby on Rails. DEV Community © 2016 - 2023. We're a place where coders share, stay up-to-date and grow their careers.



This post first appeared on VedVyas Articles, please read the originial post: here

Share the post

Rust, Go, Rinha e I/O

×

Subscribe to Vedvyas Articles

Get updates delivered right to your inbox!

Thank you for your subscription

×