<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="pt-br"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://gilsondev.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://gilsondev.github.io/" rel="alternate" type="text/html" hreflang="pt-br" /><updated>2026-10-27T00:44:21+00:00</updated><id>https://gilsondev.github.io/feed.xml</id><title type="html">Gilson Filho</title><subtitle>Criando soluções usando tecnologia
</subtitle><author><name>Gilson Filho</name><email>me@gilsondev.in</email></author><entry><title type="html">Links da Semana #2</title><link href="https://gilsondev.github.io/dicas/2019/06/28/links-da-semana-2/" rel="alternate" type="text/html" title="Links da Semana #2" /><published>2019-06-28T00:00:00+00:00</published><updated>2019-06-28T00:00:00+00:00</updated><id>https://gilsondev.github.io/dicas/2019/06/28/links-da-semana-2</id><content type="html" xml:base="https://gilsondev.github.io/dicas/2019/06/28/links-da-semana-2/"><![CDATA[<p>Olá pessoal, vamos para as dicas da semana? :)</p>

<h3 id="httpbin"><a href="http://httpbin.org/">Httpbin</a></h3>

<p><img src="/assets/img/posts/week-links/week_2/Screenshot_20190627_090231-84beb6b4-2472-4f9e-9b26-c059b08ecc39.png" alt="" /></p>

<p>É um app que oferece serviços HTTP em geral, como requisições e respostas, testes em diferentes tipos de verbos métodos de autenticação, etc. Ele é interessante quando precisa de um componente que atue como backend para algum teste como API REST genérico, serviço de autenticação, etc.</p>

<h3 id="notion"><a href="https://notion.so">Notion</a></h3>

<p><img src="/assets/img/posts/week-links/week_2/Peek_27-06-2019_08-54-a8771e8d-9d36-47d3-9550-e5eff7efb016.gif" alt="" /></p>

<p>A algum tempo estava procurando um serviço que poderia oferecer os recursos que o famoso Evernote tem, mas com diagramações e organizações bacanas para se montar um commonplace, ou seja, uma wiki pessoal para poder centralizar conteúdo que adquiro no trabalho, estudos, etc. Nessa busca encontrei o Notion, e olha, nem pensei duas vezes de deixar o Evernote. Ele oferece várias funções e seu foco é poder ter senão tudo, boa parte dos serviços que estamos acostumados a usar (Google Docs, Trello, Evernote, GIthub Wiki, Google Sheets, entre outros). Ele tem um plano gratuito, mas o plano pago mais simples é um preço muito bom em relação ao que eles oferecem.</p>

<p>Vale o teste.</p>

<h3 id="palestra-accountability---a-evolução-da-responsabilidade"><a href="https://www.infoq.com/br/presentations/accountability-a-evolucao-da-responsabilidade">Palestra: Accountability - A Evolução da Responsabilidade</a></h3>

<p><img src="/assets/img/posts/week-links/week_2/Screenshot_20190627_090128-48743180-72c2-4c9b-87be-3d4c9147af74.png" alt="" /></p>

<p>Esse termo Accountability acabei conhecendo em uma startup que trabalhei em São Paulo, e achei muito interessante o significado por trás, que apesar de traduzirmos livremente como responsabilidade, na prática ele traz algo mais profundo que isso. Essa palestra pretende trazer essa explicação, e como isso é essencial em times ágeis que são auto-organizáveis e que precisa de integrantes engajados nos desafios dos projetos.</p>

<h2 id="terminal-com-oh-my-zsh-spaceship-dracula-e-mais"><a href="https://blog.rocketseat.com.br/terminal-com-oh-my-zsh-spaceship-dracula-e-mais/">Terminal com Oh My Zsh, Spaceship, Dracula e mais</a></h2>

<p><img src="/assets/img/posts/week-links/week_2/Screenshot_20190628_140224-be52a461-fc68-4499-89b8-9957fad7cdb9.png" alt="" /></p>

<p>Configurar terminal é uma arte, e cada um faz do jeito que achar melhor. Esse post oferece outras formas de customizar e tornar ele mais rico no uso do dia a dia, como usar o ZSH e o conhecido Oh-my-ZSH e temas que ultrapassaram o limite de prompts de comando e IDEs, como o Dracula.</p>

<p>Até a próxima semana ;)</p>]]></content><author><name>Gilson Filho</name><email>me@gilsondev.in</email></author><category term="dicas" /><category term="dados" /><category term="desenvolvimento" /><category term="data science" /><category term="banco de dados" /><category term="cursos" /><category term="docker" /><category term="kubernetes" /><category term="katacoda" /><summary type="html"><![CDATA[Olá pessoal, vamos para as dicas da semana? :)]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://gilsondev.github.io/assets/img/posts/week-links/cover_week_links_1.jpg" /><media:content medium="image" url="https://gilsondev.github.io/assets/img/posts/week-links/cover_week_links_1.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Mensageria de Alta Performance com Apache Kafka #2</title><link href="https://gilsondev.github.io/engenharia-de-dados/2019/06/24/mensageria-de-alta-performance-com-apache-kafka-2/" rel="alternate" type="text/html" title="Mensageria de Alta Performance com Apache Kafka #2" /><published>2019-06-24T00:00:00+00:00</published><updated>2019-06-24T00:00:00+00:00</updated><id>https://gilsondev.github.io/engenharia-de-dados/2019/06/24/mensageria-de-alta-performance-com-apache-kafka-2</id><content type="html" xml:base="https://gilsondev.github.io/engenharia-de-dados/2019/06/24/mensageria-de-alta-performance-com-apache-kafka-2/"><![CDATA[<p>Olá pessoal, nesse segundo post da série vamos abordar mais sobre os tópicos no Kafka e como é sua estrutura, além de mostrar as operações a serem feitos para mantê-las e as ações de um producer e consumer.</p>

<h2 id="tópicos-e-partições">Tópicos e Partições</h2>

<p>O Kafka oferece binários para podemos efetuar operações nele, e uma delas é relacionado aos tópicos. Um tópico é nada mais que um fluxo de dados nomeado, e esse tópico armazena logs e são distribuídos e particionados, ou seja, divididos em pequenas partes. Pense no tópico como uma categoria ou um feed em que os registros são publicados.</p>

<p><img src="/assets/img/posts/serie_kafka_2/image1.png" alt="" /></p>

<p>Cada partição possui uma sequência ordenada e imutável, que é continuamente anexado (<em>appended</em>) para uma estrutura de log. Como pode ver na imagem acima, cada registro na partição tem um número sequencial atribuído, chamado de <em>offset</em>, que identifica unicamente cada registro da partição.</p>

<p>O cluster Kafka persiste todos os registros publicados, mesmo sendo consumidos ou não, usando uma configuração de retenção. Por exemplo, se na configuração foi setada que a política de retenção é de dois dias, então dentro desse período os registro serão inseridos nos tópicos e mantidos. Caso passe dos dois dias, eles serão descartados.</p>

<p>Uma característica interessante do Kafka é também armazenar metadados por consumer, que são o deslocamento ou a posição dele no log. Veja a imagem abaixo:</p>

<p><img src="/assets/img/posts/serie_kafka_2/image2.png" alt="" /></p>

<p>Esse <em>offset</em> é controlado pelo consumer, então ele avança linearmente para consumir os registros, mas se ele quiser resetar esse metadado para reprocessar os dados ele pode, ou pular para o registro mais recente e começar a consumir a partir de “agora”, isso é possível.</p>

<h2 id="operações-nos-tópicos">Operações nos Tópicos</h2>

<p>Por meio do <code class="language-plaintext highlighter-rouge">kafka-topics</code> podemos criar, listar, alterar e remover tópicos quando necessário. Abaixo vamos mostrar como efetuar cada operação, e os detalhes de cada parâmetro podemos explicar mais a frente.</p>

<p><strong>Criando um tópico</strong></p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kafka-topics <span class="nt">--zookeeper</span> 127.0.0.1:2181 <span class="nt">--create</span> <span class="nt">--topic</span> first_topic <span class="nt">--partition</span> 3 <span class="nt">--replication-factor</span> 1
</code></pre></div></div>

<p>A sáida deve ser parecida com essa:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Created topic "first_topic"
</code></pre></div></div>

<p>Se tentar criar um tópico com o mesmo nome, o Kafka não vai autorizar porque cada nome de tópico é único. Será preciso removê-la para criar outro com o mesmo nome.</p>

<p><strong>Listando tópicos</strong></p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kafka-topics <span class="nt">--zookeeper</span> 127.0.0.1:2181 <span class="nt">--list</span>
</code></pre></div></div>

<p>A saída deve ser parecida com essa:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>__consumer_offsets
logs-broker
first_topic
</code></pre></div></div>

<p><strong>Removendo tópicos</strong></p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kafka-topics <span class="nt">--zookeeper</span> 127.0.0.1:2181 <span class="nt">--topic</span> first_topic <span class="nt">--delete</span>
</code></pre></div></div>

<p>A saída deve ser parecida com essa:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Topic first_topic is marked for deletion
Note: This will have no impact if delete.topic.enable is not set to True
</code></pre></div></div>

<p>O tópico não é removido se:</p>

<ul>
  <li>Possuir producers vinculados</li>
  <li>Possuir consumers vinculados</li>
  <li>Tiver registros salvos</li>
</ul>

<p>Uma das <a href="https://stackoverflow.com/questions/33537950/how-to-delete-a-topic-in-apache-kafka/33538299">formas de remover definitivamente</a> é reiniciando e removendo fisicamente o diretório do tópico.</p>

<p><strong>Detalhando tópico</strong></p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kafka-topics <span class="nt">--zookeeper</span> 127.0.0.1:2181 <span class="nt">--topic</span> first_topic <span class="nt">--describe</span>
</code></pre></div></div>

<p>A saída deve ser parecida com essa:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Topic:first_topic       PartitionCount:3        ReplicationFactor:1     Configs:
        Topic: first_topic      Partition: 0    Leader: 0       Replicas: 0     Isr: 0
        Topic: first_topic      Partition: 1    Leader: 0       Replicas: 0     Isr: 0
        Topic: first_topic      Partition: 2    Leader: 0       Replicas: 0     Isr: 0
</code></pre></div></div>

<hr />

<h2 id="enviando-mensagens-com-producers">Enviando mensagens com Producers</h2>

<p>Vamos fazer o primeiro envio de mensagens ao tópico que acabamos de criar na seção anterior. O Kafka possui um script chamado de <a href="http://kafka-console-producer.sh/"><code class="language-plaintext highlighter-rouge">kafka-console-producer.sh</code></a> em que faz o papel de um producer. Abaixo está o comando que vamos executar para enviar um texto de exemplo:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kafka-console-producer.sh <span class="nt">--broker-list</span> localhost:9092 <span class="nt">--topic</span> first_topic
This is a message
This is another message
</code></pre></div></div>

<p>Quando damos enter no comando, ele abre a comunicação no terminal, para receber o conteúdo e assim enviar ao tópico. a cada texto inserido seguindo de <ENTER> será uma mensagem enviada, então no exemplo acima foi enviado duas informações.</ENTER></p>

<h2 id="consumindo-mensagens-com-consumers">Consumindo mensagens com Consumers</h2>

<p>Vamos seguir a mesma idéia, só que para o consumo das mensagens enviadas. No mesmo diretório de binários do Kafka, tem o script <a href="http://kafka-console-consumer.sh/"><code class="language-plaintext highlighter-rouge">kafka-console-consumer.sh</code></a> em que atua como um consumidor de um determinado tópico. O comando abaixo exemplifica o acesso as mensagens do tópico <em>first_topic</em>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic first_topic --from-beginning
This is a message
This is another message
</code></pre></div></div>

<p>Ele abre também a comunicação no terminal, só que para a saída padrão. O comando pediu para o Kafka consumir as mensagens do tópico <strong>desde o início</strong>. Isso é importante porque o consumer pode escolher acessar as informações da seguinte forma:</p>

<ul>
  <li><strong>Início do tópico (earliest)</strong>: Consome as mensagens partindo do offset 0 de todas as partições, ou de uma específica</li>
  <li><strong>Fim do tópico (latest)</strong>: Consome as mensagens partindo do último offset de todas as partições ou de uma específica</li>
  <li><strong>Offset específico</strong>: Consome mensagem de um offset ID específico que foi definido pelo consumidor, então nesse caso precisa saber o seu índice.</li>
</ul>

<p>Vamos mostrar alguns exemplos:</p>

<ul>
  <li><strong>Consumindo a partir do offset inicial, de todas as partições</strong></li>
</ul>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kafka-console-consumer.sh <span class="nt">--bootstrap-server</span> localhost:9092 <span class="nt">--topic</span> first_topic <span class="nt">--offset</span> earliest
This is a message
This is another message
</code></pre></div></div>

<ul>
  <li><strong>Consumindo a partir do offset inicial, de uma partição específica</strong></li>
</ul>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kafka-console-consumer.sh <span class="nt">--bootstrap-server</span> localhost:9092 <span class="nt">--topic</span> first_topic <span class="nt">--partition</span> 0 <span class="nt">--offset</span> earliest
This is a message
</code></pre></div></div>

<ul>
  <li><strong>Consumindo a partir do offset final, de todas as partições</strong></li>
</ul>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kafka-console-consumer.sh <span class="nt">--bootstrap-server</span> localhost:9092 <span class="nt">--topic</span> first_topic <span class="nt">--offset</span> latest
Message sended after the two first
</code></pre></div></div>

<ul>
  <li><strong>Consumindo a partir do offset final, de uma partição específica</strong></li>
</ul>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kafka-console-consumer.sh <span class="nt">--bootstrap-server</span> localhost:9092 <span class="nt">--topic</span> first_topic <span class="nt">--partition</span> 1 <span class="nt">--offset</span> latest
This is another message
Message sended after the two first
</code></pre></div></div>

<ul>
  <li><strong>Consumindo a partir de um offset específico (índice 1)</strong></li>
</ul>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kafka-console-consumer.sh <span class="nt">--bootstrap-server</span> localhost:9092 <span class="nt">--topic</span> first_topic <span class="nt">--offset</span> 1
This is another message
</code></pre></div></div>

<p>Com as informações abordadas aqui, já podemos fazer uso da ferramenta para atuar como broker de mensagens. No terceiro post vamos aprofundar sobre os Consumer Groups, e como isso é importante no consumo de grande volume de dados.</p>]]></content><author><name>Gilson Filho</name><email>me@gilsondev.in</email></author><category term="engenharia-de-dados" /><category term="dados" /><category term="engenharia de dados" /><category term="desenvolvimento" /><category term="data science" /><category term="mensageria" /><category term="kafka" /><category term="pubsub" /><summary type="html"><![CDATA[Olá pessoal, nesse segundo post da série vamos abordar mais sobre os tópicos no Kafka e como é sua estrutura, além de mostrar as operações a serem feitos para mantê-las e as ações de um producer e consumer.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://gilsondev.github.io/assets/img/posts/serie_kafka_2/cover.jpg" /><media:content medium="image" url="https://gilsondev.github.io/assets/img/posts/serie_kafka_2/cover.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Mensageria de Alta Performance com Apache Kafka #1</title><link href="https://gilsondev.github.io/engenharia-de-dados/2019/06/22/mensageria-de-alta-performance-com-apache-kafka-1/" rel="alternate" type="text/html" title="Mensageria de Alta Performance com Apache Kafka #1" /><published>2019-06-22T00:00:00+00:00</published><updated>2019-06-22T00:00:00+00:00</updated><id>https://gilsondev.github.io/engenharia-de-dados/2019/06/22/mensageria-de-alta-performance-com-apache-kafka-1</id><content type="html" xml:base="https://gilsondev.github.io/engenharia-de-dados/2019/06/22/mensageria-de-alta-performance-com-apache-kafka-1/"><![CDATA[<p>Olá pessoal, atualmente tenho entrado na área de Ciência de Dados diante do projeto que estou trabalhando no Banco do Brasil, e a partir dessa série pretendo trazer os conhecimentos adquiridos em uma ferramenta que atua como barramento de um grande volume de mensagens, e hoje iremos falar sobre o Apache Kafka.</p>

<h3 id="o-que-é-o-apache-kafka">O que é o Apache Kafka?</h3>

<p>O Apache Kafka é uma plataforma de streaming de eventos distribuída, ou seja, ele possui três capacidades chave:</p>

<ul>
  <li>Faz <em>publish</em> e <em>subscribe</em> de streaming de dados, simular a uma fila de mensagens ou sistema de mensageria enterprise.</li>
  <li>Armazena streaming de dados em um modo <em>fault-tolerant</em> (tolerante a falhas)</li>
  <li>Processa stream de dados</li>
</ul>

<p>Ele é geral usado para dois tipos de aplicações:</p>

<ul>
  <li>Construção de pipeline de streaming de dados em tempo real que precisa de dados confiáveis entre sistemas ou aplicações</li>
  <li>Construção de aplicações em tempo real que transforma ou reage com o fluxo de dados</li>
  <li>Arquiteturas de microserviços que faz uso de um sistema de mensageria para a comunicação entre os módulos de uma aplicação dessa modalidade, e que precisa de uma alta velocidade.</li>
</ul>

<p><img src="/assets/img/posts/serie_kafka_1/tipos_uso_kafka.png" alt="" /></p>

<p>Fonte: <a href="https://www.confluent.io/what-is-apache-kafka/">https://www.confluent.io/what-is-apache-kafka/</a></p>

<h3 id="arquitetura-pubsub">Arquitetura Pub/Sub</h3>

<p>O Kafka segue a arquitetura de mensageria no estilo Publisher/Consumer, em que um componente <strong>publica</strong> uma mensagem em uma fila chamado de <strong>tópico</strong> a um intermediário e um outro componente <strong>consome</strong> essa mensagem a partir do intermediário, no <strong>tópico</strong> que essa informação está. Essas mensagens não são direcionadas a determinados consumidores, então para que eles possam acessar os dados eles se <strong>inscrevem</strong> nessa fila do intemediário para aguardarem novas informacões.</p>

<ul>
  <li><strong>Publisher</strong>: responsável por publicar as mensagens a um broker</li>
  <li><strong>Broker</strong>: intermediário entre um Publisher e Consumer, que no nosso caso é o <strong>Kafka</strong></li>
  <li><strong>Tópico</strong>: Recurso nomeado para qual as mensagens são enviadas</li>
  <li><strong>Consumer</strong>: responsável por consumir as mensagens de um determinado tópico</li>
</ul>

<p><img src="/assets/img/posts/serie_kafka_1/kafka_producer_consumer.png" alt="" /></p>

<p>Fonte: <a href="https://www.cloudkarafka.com/blog/2016-11-30-part1-kafka-for-beginners-what-is-apache-kafka.html">https://www.cloudkarafka.com/blog/2016-11-30-part1-kafka-for-beginners-what-is-apache-kafka.html</a></p>

<h3 id="instalação-e-configuração">Instalação e Configuração</h3>

<p>O Kafka precisa do <a href="https://zookeeper.apache.org/">Apache Zookeeper</a> para coordenar os brokers do Kafka, principalmente quando envolve <a href="https://kafka.apache.org/documentation/#quickstart_multibroker">múltiplos brokers</a> em um ambiente clusterizado. Então siga as <a href="https://zookeeper.apache.org/doc/r3.5.5/zookeeperStarted.html">instruções de instalação do Zookeeper</a> antes de seguir nos procedimentos abaixo.</p>

<ul>
  <li>Faça o <a href="https://kafka.apache.org/downloads">download</a> do Apache Kafka e descompacte no diretório desejado. No nosso caso tanto o Zookeeper como o Kafka estará no <em>/opt</em>:</li>
</ul>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd</span> /opt
<span class="nv">$ </span>wget https://www-eu.apache.org/dist/kafka/2.2.1/kafka_2.11-2.2.1.tgz
<span class="nv">$ </span><span class="nb">tar</span> <span class="nt">-zxvf</span> kafka_2.11-2.2.1.tgz
<span class="nv">$ </span><span class="nb">mv </span>kafka_2.11-2.2.1 kafka
</code></pre></div></div>

<ul>
  <li>Inicie o servidor Zookeeper caso não tenha feito, e após isso inicie o servidor do Kafka:</li>
</ul>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Iniciando Zookeeper</span>

<span class="nv">$ </span><span class="nb">cd</span> /opt/zookeeper
<span class="nv">$ </span>./bin/zkServer.sh start

<span class="c"># Iniciando Kafka</span>

<span class="nv">$ </span><span class="nb">cd</span> /opt/kafka
<span class="nv">$ </span>./bin/kafka-server-start.sh config/server.properties
</code></pre></div></div>

<p>Para confirmar se estão de pé, verifique se na sua máquina tem as seguintes portas abaixo:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>netstat <span class="nt">-tlpn</span> | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"2181|9092"</span>
tcp6       0      0 :::9092                 :::<span class="k">*</span>                    OUÇA       6531/java
tcp6       0      0 :::2181                 :::<span class="k">*</span>                    OUÇA       6498/java
</code></pre></div></div>

<p>A porta 2181 é do Apache Zookeeper e o 9092 é a porta padrão do Apache Kafka.</p>

<h3 id="conceitos-do-kafka">Conceitos do Kafka</h3>

<p>Ele possui alguns conceitos que são relevantes saber:</p>

<ul>
  <li>Kafka é executado como <strong>um cluster de um ou mais servidores</strong> que pode abranger para múltiplos <strong>datacenters</strong></li>
  <li>Esse cluster armazena fluxos de dados em categorias chamados de <strong><em>topics</em></strong>.</li>
  <li>Cada registro consistem de uma <strong>chave</strong>, um <strong>valor</strong> e um <strong>timestamp</strong>.</li>
</ul>

<p>Ele é composto por quatro APIs:</p>

<ul>
  <li>O <a href="https://kafka.apache.org/documentation.html#producerapi"><strong>Producer API</strong></a> faz com que uma aplicação possa publicar um fluxo de registros para um ou mais tópicos Kafka</li>
  <li>O <a href="https://kafka.apache.org/documentation.html#consumerapi"><strong>Consumer API</strong></a> faz com que a aplicação possa assinar um ou mais tópicos e processar o fluxo de registros produzidos por alguém.</li>
  <li>O <a href="https://kafka.apache.org/documentation/streams"><strong>Streams API</strong></a> faz com que a aplicação possa atuar como um <em>stream processor</em>, consumindo um fluxo de entrada (<em>input stream</em>) de um ou mais tópicos e produzir um fluxo de saída (<em>output stream</em>) para um ou mais tópicos, transformando de forma eficiente.</li>
  <li>O <a href="https://kafka.apache.org/documentation.html#connect"><strong>Connector API</strong></a> permite construir e executar <em>producers</em> ou <em>consumers</em> reutilizáveis que conecta tópicos a aplicativos ou sistemas de dados existentes. Por exemplo, um conector para um banco relacional captura cada mudança de uma tabela.</li>
</ul>

<p>A comunicação entre os clientes e servidores é feito de forma simples e com alta performance, usando o protocolo TCP. Além disso, o Kafka provêm um cliente Java, mas tem disponíveis <a href="https://cwiki.apache.org/confluence/display/KAFKA/Clients">outros clientes para várias linguagens</a>.</p>

<p><img src="/assets/img/posts/serie_kafka_1/kafka-apis.png" alt="" /></p>

<p>Fonte: <a href="https://kafka.apache.org/intro">https://kafka.apache.org/intro</a></p>

<p>No próximo post, vamos falar mais sobre os tópicos e qual é a idéia das partições no Kafka. Além disso vou mostrar como podemos criar, alterar e remover tótpicos, como também atuar na ação de publicar e consumir mensagens de um tópico, e como fazemos com que mais de um consumer possa acessar o tópico sem buscar dados duplicados, por meio de <em>consumer group</em>.</p>

<p>Até mais! ;)</p>]]></content><author><name>Gilson Filho</name><email>me@gilsondev.in</email></author><category term="engenharia-de-dados" /><category term="dados" /><category term="engenharia de dados" /><category term="desenvolvimento" /><category term="data science" /><category term="mensageria" /><category term="kafka" /><category term="pubsub" /><summary type="html"><![CDATA[Olá pessoal, atualmente tenho entrado na área de Ciência de Dados diante do projeto que estou trabalhando no Banco do Brasil, e a partir dessa série pretendo trazer os conhecimentos adquiridos em uma ferramenta que atua como barramento de um grande volume de mensagens, e hoje iremos falar sobre o Apache Kafka.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://gilsondev.github.io/assets/img/posts/serie_kafka_1/cover.jpg" /><media:content medium="image" url="https://gilsondev.github.io/assets/img/posts/serie_kafka_1/cover.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Links da Semana #1</title><link href="https://gilsondev.github.io/dicas/2019/06/21/links-da-semana-1/" rel="alternate" type="text/html" title="Links da Semana #1" /><published>2019-06-21T00:00:00+00:00</published><updated>2019-06-21T00:00:00+00:00</updated><id>https://gilsondev.github.io/dicas/2019/06/21/links-da-semana-1</id><content type="html" xml:base="https://gilsondev.github.io/dicas/2019/06/21/links-da-semana-1/"><![CDATA[<p>Olá pessoal, esse é o primeiro de uma série de posts, com o objetivo de compartilhar links de posts, projetos, palestra, entre outras informações relevantes em qualquer ãrea.</p>

<h3 id="professional-programming"><a href="https://github.com/charlax/professional-programming">Professional Programming</a></h3>

<p><img src="/assets/img/posts/week-links/week_1/link1.png" alt="" /></p>

<p>Esse repositório foi criado para listar todo um material em que todo desenvolvedor em algum momento da carreira precisa conhecer. Ele possui referências conhecidas como Programatic Programmer, Code Complete para uma abordagem geral e até voltado para áreas específicas como Ciência de Dados, Desenvolvimento Web, Algoritmos, entre outros.</p>

<p>Se em algum momento quer saber qual tipo de conteúdo e literaturas que são recomendados para crescer na sua carreira, recomendo consultar nesse projeto que vai encontrar ótimas referências de obras.</p>

<h3 id="emag-ética"><a href="https://www.infoq.com/br/minibooks/emag-etica/">eMag: Ética</a></h3>

<p><img src="/assets/img/posts/week-links/week_1/link2.png" alt="" /></p>

<p>A InfoQ a algum tempo tem publicado ótimas edições de revistas que focam em temas específicos como Java, IoT e esse não seria diferente. Destoando dos assuntos mais técnicos, essa edição aborda em algo que é cada vez mais importante discutirmos, a ética na área de TI. Nessa revista inclui assuntos como:</p>

<ul>
  <li><strong>Ética dos dados</strong> e como os cidadãos são impactados com os novos modelos de negócios que o mundo da Ciência de Dados tem proporcionado</li>
  <li>A repercussão no resultado da <strong>Pesquisa para Desenvolvedores da Stack Overflow</strong> que pela primeira vez incluiu <strong>perguntas sobre ética</strong>.</li>
  <li>A <strong>perspectiva psicológica</strong> da ética diante de tecnologias como <strong>Machine Learning</strong></li>
  <li>Ética da <strong>segurança da informação</strong></li>
  <li>E qual é o <strong>próximo passo</strong> sobre esse tema na área de TI</li>
</ul>

<h3 id="repositório-de-estudos-de-data-science"><a href="https://github.com/paulinhacnn/Data-Science">Repositório de estudos de Data Science</a></h3>

<p><img src="/assets/img/posts/week-links/week_1/link3.png" alt="" /></p>

<p>A usuária <a href="https://github.com/paulinhacnn">paulinhacnn</a> criou um repositório com o objetivo de toda semana colocar um novo material para ajudar quem deseja entrar na área de ciência de dados, como estatística, que é um tema que senão todos, uma boa parte dos profissionais sentem que precisam começar.</p>

<h3 id="katacoda-plataforma-de-estudo-interativo"><a href="https://www.katacoda.com/">Katacoda: Plataforma de estudo interativo</a></h3>

<p><img src="/assets/img/posts/week-links/week_1/link4.png" alt="" /></p>

<p>Toda plataforma que possui cursos acessíveis para aprendermos uma nova tecnologia quando nós precisamos, é sempre bem vindo. O Katacoda é uma plataforma de estudo, em que você já usa a prática nos estudos, como preparar ambientes Kubernetes, criar e executar códigos-fonte, entre outras formas. No momento dessa postagem, ele possui cursos como:</p>

<ul>
  <li>Docker</li>
  <li>Kubernetes</li>
  <li>Swarm</li>
  <li>Prometheus</li>
  <li>Go</li>
  <li>Java</li>
  <li>Jenkins</li>
</ul>

<p>E vários outros que ajuda é criar expertise em infraestrutura devops e que tem ênfase em cloud-native e microserviços.</p>

<h3 id="dbdiagramio-ferramenta-para-criação-de-diagramas-er-com-código"><a href="https://dbdiagram.io/home">dbdiagram.io: Ferramenta para criação de diagramas ER com código</a></h3>

<p><img src="/assets/img/posts/week-links/week_1/link5.png" alt="" /></p>

<p>Em algum momento da nossa vida como desenvolvedor, é interessante criar um diagrama ER para termos uma visão do banco de dados que vamos manipular, e sempre deparamos com ferramentas que são para bancos específicos, e fora que muitas delas são pagas.</p>

<p>Esse sistema é muito interessante porque ele é agnóstico ao tipo de banco de dados que vai usar, como também suporte uma sintaxe específica mas intuitiva de criar o seu diagrama, sem precisar clicar e arrastar a todo momento. Ela é uma boa pedida quando quer criar diagramas pontuais e que precisa de alguma solução prática.</p>

<p>Até a próxima semana ;)</p>]]></content><author><name>Gilson Filho</name><email>me@gilsondev.in</email></author><category term="dicas" /><category term="dados" /><category term="desenvolvimento" /><category term="data science" /><category term="banco de dados" /><category term="cursos" /><category term="docker" /><category term="kubernetes" /><category term="katacoda" /><summary type="html"><![CDATA[Olá pessoal, esse é o primeiro de uma série de posts, com o objetivo de compartilhar links de posts, projetos, palestra, entre outras informações relevantes em qualquer ãrea.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://gilsondev.github.io/assets/img/posts/week-links/cover_week_links_1.jpg" /><media:content medium="image" url="https://gilsondev.github.io/assets/img/posts/week-links/cover_week_links_1.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Usando o Airflow no Heroku</title><link href="https://gilsondev.github.io/airflow/dados/2018/12/24/usando-o-airflow-no-heroku/" rel="alternate" type="text/html" title="Usando o Airflow no Heroku" /><published>2018-12-24T00:00:00+00:00</published><updated>2018-12-24T00:00:00+00:00</updated><id>https://gilsondev.github.io/airflow/dados/2018/12/24/usando-o-airflow-no-heroku</id><content type="html" xml:base="https://gilsondev.github.io/airflow/dados/2018/12/24/usando-o-airflow-no-heroku/"><![CDATA[<p>No post de hoje vamos mostrar como você pode usar o Airflow no famoso serviço Heroku, e como é simples fazer isso.</p>

<h2 id="preparando-o-ambiente">Preparando o ambiente</h2>

<p>Antes de mais nada vamos deixar tudo pronto para podemos fazer isso, tanto no ambiente local como no Heroku. Primeiro você precisa ter os requisitos abaixo para conseguir seguir tudo o que está aqui sem problemas:</p>

<ul>
  <li>Heroku Toolbelt</li>
  <li>Python 3</li>
  <li>Virtualenv</li>
</ul>

<p>Se está com os itens acima, vamos começar. Primeiro crie um ambiente virtual para instalarmos as dependências necessárias:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">mkdir </span>airflow-heroku <span class="o">&amp;&amp;</span> <span class="nb">cd </span>airflow-heroku
<span class="nv">$ </span>python3.6 <span class="nt">-m</span> venv .venv
</code></pre></div></div>

<p>Ative o ambiente e instale as dependências. Não esqueça de salvar no arquivo <code class="language-plaintext highlighter-rouge">requirements.txt</code> para que o Heroku saiba o que instalar:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">source</span> .venv/bin/activate
<span class="nv">$ </span>pip <span class="nb">install</span> <span class="s2">"apache-airflow[postgres,password]"</span> cryptography
<span class="nv">$ </span>pip freeze <span class="o">&gt;</span> requirements.txt
</code></pre></div></div>

<p>Agora precisamos usar o <code class="language-plaintext highlighter-rouge">airflow init</code> para criar os arquivos necessários para executarmos o ambiente localmente. O que vamos precisar mais na frente para o deploy é o arquivo <code class="language-plaintext highlighter-rouge">airflow.cfg</code>:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">export </span><span class="nv">AIRFLOW_HOME</span><span class="o">=</span><span class="nv">$PWD</span>  <span class="c"># Definindo a raiz do airflow na pasta corrente</span>
<span class="nv">$ </span>airflow initdb
</code></pre></div></div>

<p>O airflow irá criar vários arquivos e pastas como o exemplo abaixo:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">.</span>
├── airflow.cfg
├── airflow.db
├── logs
└── unittests.cfg
</code></pre></div></div>

<p>Vamos aproveitar o momento e preparar o repositório do projeto, como também incluir
os arquivos que não serão versionados:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git init
<span class="nv">$ </span><span class="nb">cat</span> <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh"> &gt;&gt; .gitignore
*.pyc
*.db
.venv/
logs/
</span><span class="no">EOF
</span></code></pre></div></div>

<p><strong>Observação</strong>: Ao executar com Python 3.7, pode ocorrer de aparecer a seguinte saída:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ERROR [airflow.models.DagBag] Failed to import: /tmp/airflow-heroku/.venv/lib/python3.7/site-packages/airflow/example_dags/example_http_operator.py
Traceback (most recent call last):
  File "/tmp/airflow-heroku/.venv/lib/python3.7/site-packages/airflow/models.py", line 377, in process_file
    m = imp.load_source(mod_name, filepath)
  File "/home/gilson/.pyenv/versions/3.7.1/lib/python3.7/imp.py", line 171, in load_source
    module = _load(spec)
  File "&lt;frozen importlib._bootstrap&gt;", line 696, in _load
  File "&lt;frozen importlib._bootstrap&gt;", line 677, in _load_unlocked
  File "&lt;frozen importlib._bootstrap_external&gt;", line 728, in exec_module
  File "&lt;frozen importlib._bootstrap&gt;", line 219, in _call_with_frames_removed
  File "/tmp/airflow-heroku/.venv/lib/python3.7/site-packages/airflow/example_dags/example_http_operator.py", line 27, in &lt;module&gt;
    from airflow.operators.http_operator import SimpleHttpOperator
  File "/tmp/airflow-heroku/.venv/lib/python3.7/site-packages/airflow/operators/http_operator.py", line 21, in &lt;module&gt;
    from airflow.hooks.http_hook import HttpHook
  File "/tmp/airflow-heroku/.venv/lib/python3.7/site-packages/airflow/hooks/http_hook.py", line 23, in &lt;module&gt;
    import tenacity
  File "/tmp/airflow-heroku/.venv/lib/python3.7/site-packages/tenacity/__init__.py", line 352
    from tenacity.async import AsyncRetrying
                      ^
SyntaxError: invalid syntax
</code></pre></div></div>

<p>Mesmo assim os arquivos serão criados e será executado sem problemas. O erro acontece por conta de uma DAG de exemplo.</p>

<p>Por fim, crie o arquivo <code class="language-plaintext highlighter-rouge">generate_fernet_key.py</code> e insira na pasta <code class="language-plaintext highlighter-rouge">contrib</code>. Ela irá gerar uma chave para que o Airflow use para encriptação de dados sensíveis. Insira o conteúdo abaixo no módulo citado:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python
</span>
<span class="kn">from</span> <span class="nn">cryptography</span> <span class="kn">import</span> <span class="n">fernet</span>

<span class="k">print</span><span class="p">(</span><span class="n">fernet</span><span class="p">.</span><span class="n">Fernet</span><span class="p">.</span><span class="n">generate_key</span><span class="p">().</span><span class="n">decode</span><span class="p">(</span><span class="s">"utf-8"</span><span class="p">))</span>
</code></pre></div></div>

<p>Dessa forma que preparamos o ambiente, já podemos criar as DAGS e fazer o teste rodando localmente.</p>

<h2 id="tornando-o-ambiente-mais-semelhante-ao-de-produção">Tornando o ambiente mais semelhante ao de produção</h2>

<p>Mesmo o heroku oferecendo toda a facilidade de implantação, é uma boa ter localmente o ambiente minimamente parecido com o que as suas DAGs serão executados. Vamos fazer algumas mudanças no arquivo <code class="language-plaintext highlighter-rouge">airflow.cfg</code></p>

<p>O arquivo <code class="language-plaintext highlighter-rouge">airflow.cfg</code> apesar de versionarmos, será usado mais para uso local, então aproveite para deixar o ambiente mais próximo da produção como:</p>

<ul>
  <li>Definir um banco relacional para o Airflow como PostgreSQL</li>
  <li>Registrar o tipo de executor adequado para rodar as DAGS</li>
  <li>Gerar uma chave com Fernet por meio do <code class="language-plaintext highlighter-rouge">generate_fernet_key.py</code></li>
</ul>

<p>Faça a alteração no arquivo de configuração para inserir informações acerca do banco de dados e o tipo de <a href="https://airflow.incubator.apache.org/code.html?highlight=executors#airflow.executors.local_executor.LocalExecutor">executor desejado</a> que o Airflow irá usar para executar o agendamento dos workflows:</p>

<div class="language-config highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># ...
</span><span class="n">sql_alchemy_conn</span> = <span class="n">postgresql</span>+<span class="n">psycopg2</span>://<span class="n">postgres</span>:<span class="n">postgres</span>@<span class="n">localhost</span>:<span class="m">5432</span>/<span class="n">airflow</span>

<span class="c"># ...
</span><span class="n">executor</span> = <span class="n">LocalExecutor</span>

<span class="c"># ...
</span><span class="n">fernet_key</span> = $(<span class="n">python</span> <span class="s2">"contrib/generate_fernet_key.py"</span>)
</code></pre></div></div>

<p>Dessa forma o Airflow irá usar o banco PostgreSQL para armazenar os metadados das DAGs e suas execuções como o também o seu scheduler.</p>

<p>Remova o arquivo <code class="language-plaintext highlighter-rouge">airflow.db</code> e rode novamente o <code class="language-plaintext highlighter-rouge">airflow initdb</code> para que as migrations do airflow seja aplicados no banco.</p>

<h2 id="configurando-projeto-para-o-heroku">Configurando projeto para o Heroku</h2>

<p>Agora vamos fazer algumas mudanças para que o Airflow seja executado corretamente no Heroku, juntamente com um usuário para acessar o painel de administração da ferramenta.</p>

<p>No caso do Heroku vamos aproveitar o uso de <a href="https://airflow.apache.org/howto/set-config.html?highlight=variable%20environment#setting-configuration-options">variávels de ambiente</a> para sobrescrever propriedades do Airflow. Faça uso do <code class="language-plaintext highlighter-rouge">config:set</code>. Lembre-se de criar o app e os addons necessários:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>heroku apps:create heroku-airflow
<span class="nv">$ </span>heroku addons:create heroku-postgresql:hobby-dev
</code></pre></div></div>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>heroku config:set <span class="nv">SLUGIFY_USES_TEXT_UNIDECODE</span><span class="o">=</span><span class="nb">yes</span>
<span class="nv">$ </span>heroku config:set <span class="nv">AIRFLOW_HOME</span><span class="o">=</span><span class="s2">"/app"</span>
<span class="nv">$ </span>heroku config:set <span class="nv">AIRFLOW__CORE__DAGS_FOLDER</span><span class="o">=</span><span class="s2">"/app/dags"</span>
<span class="nv">$ </span>heroku config:set <span class="nv">AIRFLOW__CORE__FERNET_KEY</span><span class="o">=</span><span class="sb">`</span>python contrib/generate_fernet_key.py<span class="sb">`</span>
<span class="nv">$ </span>heroku config:set <span class="nv">AIRFLOW__CORE__LOAD_EXAMPLES</span><span class="o">=</span>False
<span class="nv">$ </span>heroku config:set <span class="nv">AIRFLOW__CORE__SQL_ALCHEMY_CONN</span><span class="o">=</span><span class="sb">`</span>heroku config:get DATABASE_URL<span class="sb">`</span>
<span class="nv">$ </span>heroku config:set <span class="nv">AIRFLOW__CORE__EXECUTOR</span><span class="o">=</span><span class="s2">"LocalExecutor"</span>
</code></pre></div></div>

<p>Crie o arquivo <code class="language-plaintext highlighter-rouge">Procfile</code> para o Heroku inicializar a aplicação corretamente:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>release: airflow initdb
web: airflow webserver <span class="nt">-p</span> <span class="nv">$PORT</span> <span class="nt">--daemon</span> <span class="o">&amp;&amp;</span> airflow scheduler
</code></pre></div></div>

<p>Agora vamos versionar e enviar os arquivos para o Heroku:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git add <span class="nb">.</span>
<span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s2">"Arquivos para o ambiente Airflow"</span>
<span class="nv">$ </span>git push heroku master
</code></pre></div></div>

<p>Com isso conseguimos fazer com que o Airflow execute no ambiente:</p>

<p><img src="/assets/img/posts/airflow-heroku/airflow_heroku_worked_1.png" alt="Página inicial do Airflow no Heroku" /></p>

<p>Mas como pode perceber, deixar aberto o painel dessa forma não é seguro. Vamos tornar mais seguro.</p>

<h3 id="inserindo-autenticação-no-airflow">Inserindo autenticação no Airflow</h3>

<p>Vamos inserir uma nova variável de ambiente para que o Airflow invoque a autenticação:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>heroku config:set <span class="nv">AIRFLOW__WEBSERVER__AUTHENTICATE</span><span class="o">=</span>True
heroku config:set <span class="nv">AIRFLOW__WEBSERVER__AUTH_BACKEND</span><span class="o">=</span><span class="s2">"airflow.contrib.auth.backends.password_auth"</span>
</code></pre></div></div>

<p>Reinicie o seu dyno, e então vai encontrar uma página parecida com a imagem abaixo, se seguiu os procedimentos corretamente:</p>

<p><img src="/assets/img/posts/airflow-heroku/airflow_heroku_worked_2.png" alt="Página inicial do Airflow no Heroku" /></p>

<p>Agora que está preparado para autenticar, vamos criar um novo usuário. Acesse o shell do Python no Heroku com <code class="language-plaintext highlighter-rouge">heroku run python</code> e execute os comandos abaixo:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import airflow
from airflow import models, settings
from airflow.contrib.auth.backends.password_auth import PasswordUser

user <span class="o">=</span> PasswordUser<span class="o">(</span>models.User<span class="o">())</span>
user.username <span class="o">=</span> <span class="s1">'admin'</span>
user.email <span class="o">=</span> <span class="s1">'me@gilsondev.in'</span>
user.password <span class="o">=</span> <span class="s1">'password'</span>

session <span class="o">=</span> settings.Session<span class="o">()</span>
session.add<span class="o">(</span>user<span class="o">)</span>
session.commit<span class="o">()</span>
session.close<span class="o">()</span>
<span class="nb">exit</span><span class="o">()</span>
</code></pre></div></div>

<p>Com usuário criado, se autentique com o seu username e senha e então irá acessar o painel administrativo novamente.</p>

<p><img src="/assets/img/posts/airflow-heroku/airflow_heroku_worked_1.png" alt="Página inicial do Airflow no Heroku" /></p>

<p>E pronto! Temos um airflow preparado no Heroku para receber suas DAGs.</p>

<h2 id="bônus-inserindo-sua-primeira-dag">Bônus: Inserindo sua primeira DAG</h2>

<p>Para fechar bem o post, vamos inserir uma DAG de exemplo para entenderem como usar no Airflow. Crie uma pasta <code class="language-plaintext highlighter-rouge">dags</code> na raiz do projeto e crie dentro dele um módulo chamado <code class="language-plaintext highlighter-rouge">tutorial_dag.py</code>:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s2">"""
Code that goes along with the Airflow tutorial located at:
https://github.com/apache/incubator-airflow/blob/master/airflow/example_dags/tutorial.py
"""</span>
from airflow import DAG
from airflow.operators.bash_operator import BashOperator
from datetime import datetime, timedelta


default_args <span class="o">=</span> <span class="o">{</span>
    <span class="s1">'owner'</span>: <span class="s1">'airflow'</span>,
    <span class="s1">'depends_on_past'</span>: False,
    <span class="s1">'start_date'</span>: datetime<span class="o">(</span>2015, 6, 1<span class="o">)</span>,
    <span class="s1">'email'</span>: <span class="o">[</span><span class="s1">'airflow@example.com'</span><span class="o">]</span>,
    <span class="s1">'email_on_failure'</span>: False,
    <span class="s1">'email_on_retry'</span>: False,
    <span class="s1">'retries'</span>: 1,
    <span class="s1">'retry_delay'</span>: timedelta<span class="o">(</span><span class="nv">minutes</span><span class="o">=</span>5<span class="o">)</span>,
    <span class="c"># 'queue': 'bash_queue',</span>
    <span class="c"># 'pool': 'backfill',</span>
    <span class="c"># 'priority_weight': 10,</span>
    <span class="c"># 'end_date': datetime(2016, 1, 1),</span>
<span class="o">}</span>

dag <span class="o">=</span> DAG<span class="o">(</span><span class="s1">'tutorial'</span>, <span class="nv">default_args</span><span class="o">=</span>default_args, <span class="nv">schedule_interval</span><span class="o">=</span>timedelta<span class="o">(</span><span class="nv">days</span><span class="o">=</span>1<span class="o">))</span>

<span class="c"># t1, t2 and t3 are examples of tasks created by instantiating operators</span>
t1 <span class="o">=</span> BashOperator<span class="o">(</span>
    <span class="nv">task_id</span><span class="o">=</span><span class="s1">'print_date'</span>,
    <span class="nv">bash_command</span><span class="o">=</span><span class="s1">'date'</span>,
    <span class="nv">dag</span><span class="o">=</span>dag<span class="o">)</span>

t2 <span class="o">=</span> BashOperator<span class="o">(</span>
    <span class="nv">task_id</span><span class="o">=</span><span class="s1">'sleep'</span>,
    <span class="nv">bash_command</span><span class="o">=</span><span class="s1">'sleep 5'</span>,
    <span class="nv">retries</span><span class="o">=</span>3,
    <span class="nv">dag</span><span class="o">=</span>dag<span class="o">)</span>

templated_command <span class="o">=</span> <span class="s2">"""
    
"""</span>

t3 <span class="o">=</span> BashOperator<span class="o">(</span>
    <span class="nv">task_id</span><span class="o">=</span><span class="s1">'templated'</span>,
    <span class="nv">bash_command</span><span class="o">=</span>templated_command,
    <span class="nv">params</span><span class="o">={</span><span class="s1">'my_param'</span>: <span class="s1">'Parameter I passed in'</span><span class="o">}</span>,
    <span class="nv">dag</span><span class="o">=</span>dag<span class="o">)</span>

t2.set_upstream<span class="o">(</span>t1<span class="o">)</span>
t3.set_upstream<span class="o">(</span>t1<span class="o">)</span>
</code></pre></div></div>

<p>Vamos aproveitar o código que está no <a href="https://airflow.apache.org/tutorial.html">tutorial</a> da documentação do Airflow para focarmos no funcionamento. Faça o versionamento dessa nova DAG e envie para o heroku:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git add <span class="nb">.</span>
<span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s2">"Inserção da primeira DAG"</span>
<span class="nv">$ </span>git push heroku master
</code></pre></div></div>

<p>Agora acesse novamente o seu Airflow que você verá a DAG que acabamos de criar:</p>

<p><img src="/assets/img/posts/airflow-heroku/airflow_heroku_worked_3.png" alt="Airflow com a DAG criada" /></p>

<p>E agora ativando o schedule da DAG, já começou sua execução:</p>

<p><img src="/assets/img/posts/airflow-heroku/airflow_heroku_worked_4.png" alt="Executando a DAG" /></p>

<p>Depois de vários passos, agora temos um Airflow preparado para usar no Heroku.</p>

<p>Até mais pessoal!</p>]]></content><author><name>Gilson Filho</name><email>me@gilsondev.in</email></author><category term="airflow" /><category term="dados" /><category term="python" /><category term="airflow" /><category term="workflow" /><category term="datamining" /><category term="etl" /><category term="heroku" /><summary type="html"><![CDATA[No post de hoje vamos mostrar como você pode usar o Airflow no famoso serviço Heroku, e como é simples fazer isso.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://gilsondev.github.io/assets/img/posts/airflow-heroku/cover.png" /><media:content medium="image" url="https://gilsondev.github.io/assets/img/posts/airflow-heroku/cover.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Criando testes de unmanaged models no Django</title><link href="https://gilsondev.github.io/django/2018/12/02/criando-testes-de-unmanaged-models-no-django/" rel="alternate" type="text/html" title="Criando testes de unmanaged models no Django" /><published>2018-12-02T00:00:00+00:00</published><updated>2018-12-02T00:00:00+00:00</updated><id>https://gilsondev.github.io/django/2018/12/02/criando-testes-de-unmanaged-models-no-django</id><content type="html" xml:base="https://gilsondev.github.io/django/2018/12/02/criando-testes-de-unmanaged-models-no-django/"><![CDATA[<p>No projeto que estou trabalhando no momento, precisei lidar com uma situação que era
criar modelos não-gerenciados para refletirem em tabelas já criadas no banco de dados. Até aí tudo bem,
mas como poderia criar testes unitários para validar não só o modelo mas funções de negócio que iria
precisar ficar atrelado a classe?</p>

<p>Depois de pesquisar um pouco sobre isso na <a href="https://docs.djangoproject.com/en/2.1/ref/models/options/#managed">documentação</a>, encontrei uma forma de lidar com isso
sem afetar o funcionamento real da aplicação. Mas como assim?</p>

<h2 id="como-os-unmanaged-models-funciona">Como os unmanaged models funciona?</h2>

<p>Todos os modelos que você cria no seu projeto Django, por padrão eles são <strong>managed</strong>, ou seja,
o django irá criar a tabela no banco de dados apropriadamente, baseando no que o modelo representa. Segue um exemplo:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Question</span><span class="p">(</span><span class="n">models</span><span class="p">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="n">text</span> <span class="o">=</span> <span class="n">models</span><span class="p">.</span><span class="n">TextField</span><span class="p">()</span>
</code></pre></div></div>

<p>Segue um modelo bem simples, mas que ele diz muita coisa para a ORM poder criar uma tabela como definida abaixo:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">app_question</span><span class="p">(</span>
    <span class="n">id</span> <span class="nb">int</span> <span class="k">not</span> <span class="k">null</span><span class="p">,</span>
    <span class="nb">text</span> <span class="nb">text</span> <span class="k">not</span> <span class="k">null</span><span class="p">,</span>
    <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">(</span><span class="n">id</span><span class="p">)</span>
<span class="p">);</span>
</code></pre></div></div>

<p>Caso for executar esse SQL em um banco que respeite o ANSI SQL, ela será criada. O django faz essa conversão por debaixo dos panos e preparar para a criação por meio das <a href="https://docs.djangoproject.com/en/2.1/topics/migrations/">migrations</a>. Você que conhece o framework sabe como funciona.</p>

<p>Agora pode ocorrer de a gestão desses objetos do banco de dados, como as tabelas, já existam ou são controladas fora do framework, por
inúmeros motivos, e por isso não faz sentido o Django criá-las senão o encontrará problemas, sendo que não tem como ter tabelas duplicadas. Dessa forma o framework oferece no <em>Meta</em> do seu modelo a propriedade <em>managed</em>. Se você definir como <em>False</em>, ela não será
responsável pela criação, alteração e remoção da tabela que o modelo se espelha.</p>

<p>O caso comum de uso de modelos não-gerenciáveis é quando precisa usar um banco de dados legado em que sua aplicação Django precisará
acessar. O comando <em>inspectdb</em> do <em>manage.py</em> é um ótimo auxílio para fazer o parser das tabelas desse banco para classes de modelo. Se nunca usou, recomendo que faça um teste e acredito que vai ser bem útil um dia.</p>

<h2 id="classes-não-gerenciáveis-e-os-testes">Classes não-gerenciáveis e os testes</h2>

<p>Por não serem gerenciáveis pelo Django, não quer dizer que não vai poder:</p>

<ul>
  <li>Inserir/alterar/remover registros</li>
  <li>Efetuar consultas nos registros</li>
</ul>

<p>Mas como disse antes, não poderá criar/alterar/remover a tabela. Quando você faz testes automatizados no Django e isso envolver modelos, ao rodar os testes o comportamento do framework é:</p>

<ul>
  <li>Criar tabelas no <a href="https://docs.djangoproject.com/en/2.1/topics/testing/overview/#the-test-database">banco de testes</a> usando as migrations (default em SQLite: test_nome_do_banco.sqlite)</li>
  <li>Prepara o suite de testes</li>
  <li>Faz o uso do método <em>setUp</em></li>
  <li>Executa os testes automatizados</li>
  <li>Se for usado os modelos para criar dados fake, insere na tabela recém criada</li>
  <li>Faz o uso do método <em>tearDown</em></li>
  <li>Remove os registros criados nos testes</li>
  <li>Elimina o suite de testes</li>
  <li>Remove as tabelas do banco de dados de teste</li>
</ul>

<p>Esse passo a passo foi descrito de forma bem grosseira, mas no geral é como funciona. O problema é que no caso de modelos não-gerenciados não irá funcionar porque se for criar dados fake com o modelo, vai dar erro dizendo que a tabela não existe e que o modelo não autoriza o Django a criar a tabela. Como lidar com isso?</p>

<h2 id="uma-abordagem-simples">Uma abordagem simples</h2>

<p>Buscando alternativas de lidar com isso encontrei uma solução que é <a href="https://kdazzle.svbtle.com/testing-with-unmanaged-databases-in-django">alterar o valor do managed no teste</a> ou <a href="https://dev.to/patrnk/testing-against-unmanaged-models-in-django">criar um TestRunner para isso</a>. Se um o outro vai ser melhor, vai depender do problema que quer resolver, mas a primeira é a mais prática.</p>

<p>Como assim? Digamos que temos um modelo não gerenciado que queremos testar:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">LegacyTable</span><span class="p">(</span><span class="n">models</span><span class="p">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="nb">id</span> <span class="o">=</span> <span class="n">models</span><span class="p">.</span><span class="n">PositiveIntegerField</span><span class="p">(</span><span class="n">primary_key</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">db_column</span><span class="o">=</span><span class="s">"cod_legacy"</span><span class="p">)</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="p">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">255</span><span class="p">,</span> <span class="n">db_column</span><span class="o">=</span><span class="s">"txt_name"</span><span class="p">)</span>

    <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
        <span class="n">db_table</span> <span class="o">=</span> <span class="s">"legacy_table"</span>
        <span class="n">managed</span> <span class="o">=</span> <span class="bp">False</span>
</code></pre></div></div>

<p>Criando ele e rodar o <code class="language-plaintext highlighter-rouge">makemigrations</code> para o app que o modelo está, irá criar uma migração parecido com essa:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># -*- coding: utf-8 -*-
</span><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">unicode_literals</span>

<span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">migrations</span><span class="p">,</span> <span class="n">models</span>


<span class="k">class</span> <span class="nc">Migration</span><span class="p">(</span><span class="n">migrations</span><span class="p">.</span><span class="n">Migration</span><span class="p">):</span>

    <span class="n">dependencies</span> <span class="o">=</span> <span class="p">[]</span>

    <span class="n">operations</span> <span class="o">=</span> <span class="p">[</span>
        <span class="n">migrations</span><span class="p">.</span><span class="n">CreateModel</span><span class="p">(</span>
            <span class="n">name</span><span class="o">=</span><span class="s">'LegacyTable'</span><span class="p">,</span>
            <span class="n">fields</span><span class="o">=</span><span class="p">[</span>
                <span class="p">(</span><span class="s">'id'</span><span class="p">,</span> <span class="n">models</span><span class="p">.</span><span class="n">PositiveIntegerField</span><span class="p">(</span><span class="n">db_column</span><span class="o">=</span><span class="s">'cod_legacy'</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">serialize</span><span class="o">=</span><span class="bp">False</span><span class="p">)),</span>
                <span class="p">(</span><span class="s">'name'</span><span class="p">,</span> <span class="n">models</span><span class="p">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">db_column</span><span class="o">=</span><span class="s">'txt_name'</span><span class="p">,</span> <span class="n">max_length</span><span class="o">=</span><span class="mi">255</span><span class="p">)),</span>
            <span class="p">],</span>
            <span class="n">options</span><span class="o">=</span><span class="p">{</span>
                <span class="s">'db_table'</span><span class="p">:</span> <span class="s">'legacy_table'</span><span class="p">,</span>
                <span class="s">'managed'</span><span class="p">:</span> <span class="bp">False</span><span class="p">,</span>
            <span class="p">},</span>
        <span class="p">),</span>
    <span class="p">]</span>
</code></pre></div></div>

<p>O que podemos fazer é definir uma variável no nosso <em>settings.py</em> para que caso formos rodar testes automatizados ele esteja como <em>True</em>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># settings.py
</span>
<span class="c1"># ...
</span><span class="n">DEBUG</span> <span class="o">=</span> <span class="bp">True</span>
<span class="n">TESTING</span> <span class="o">=</span> <span class="bp">True</span>
</code></pre></div></div>

<p>E com isso alteramos a migração para ficar assim:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># -*- coding: utf-8 -*-
</span><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">unicode_literals</span>

<span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">migrations</span><span class="p">,</span> <span class="n">models</span>
<span class="kn">from</span> <span class="nn">django.conf</span> <span class="kn">import</span> <span class="n">settings</span>


<span class="k">class</span> <span class="nc">Migration</span><span class="p">(</span><span class="n">migrations</span><span class="p">.</span><span class="n">Migration</span><span class="p">):</span>

    <span class="n">dependencies</span> <span class="o">=</span> <span class="p">[]</span>

    <span class="n">operations</span> <span class="o">=</span> <span class="p">[</span>
        <span class="n">migrations</span><span class="p">.</span><span class="n">CreateModel</span><span class="p">(</span>
            <span class="n">name</span><span class="o">=</span><span class="s">'LegacyTable'</span><span class="p">,</span>
            <span class="n">fields</span><span class="o">=</span><span class="p">[</span>
                <span class="p">(</span><span class="s">'id'</span><span class="p">,</span> <span class="n">models</span><span class="p">.</span><span class="n">PositiveIntegerField</span><span class="p">(</span><span class="n">db_column</span><span class="o">=</span><span class="s">'cod_legacy'</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">serialize</span><span class="o">=</span><span class="bp">False</span><span class="p">)),</span>
                <span class="p">(</span><span class="s">'name'</span><span class="p">,</span> <span class="n">models</span><span class="p">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">db_column</span><span class="o">=</span><span class="s">'txt_name'</span><span class="p">,</span> <span class="n">max_length</span><span class="o">=</span><span class="mi">255</span><span class="p">)),</span>
            <span class="p">],</span>
            <span class="n">options</span><span class="o">=</span><span class="p">{</span>
                <span class="s">'db_table'</span><span class="p">:</span> <span class="s">'legacy_table'</span><span class="p">,</span>
                <span class="s">'managed'</span><span class="p">:</span> <span class="n">settings</span><span class="p">.</span><span class="n">TESTING</span><span class="p">,</span>
            <span class="p">},</span>
        <span class="p">),</span>
    <span class="p">]</span>
</code></pre></div></div>

<p>Dessa forma, quando rodarmos <code class="language-plaintext highlighter-rouge">manage.py test app</code> a suíte de testes irá considerar como um modelo convencional, criará a tabela
no banco que será usado nos testes, e assim poderemos criar os dados fake perfeitamente. Claro que ao rodar em ambiente que não irá precisar desse tipo de situção, a propriedade <code class="language-plaintext highlighter-rouge">TESTING</code> precisa estar <code class="language-plaintext highlighter-rouge">False</code>.</p>

<p>Com isso conseguimos criar testes perfeitamente sem precisar fazer customizações malucas. Quem tiver outras soluções, coloque aqui nos comentários!</p>

<p>Até mais!</p>]]></content><author><name>Gilson Filho</name><email>me@gilsondev.in</email></author><category term="django" /><category term="python" /><category term="django" /><category term="testes" /><summary type="html"><![CDATA[No projeto que estou trabalhando no momento, precisei lidar com uma situação que era criar modelos não-gerenciados para refletirem em tabelas já criadas no banco de dados. Até aí tudo bem, mas como poderia criar testes unitários para validar não só o modelo mas funções de negócio que iria precisar ficar atrelado a classe?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://gilsondev.github.io/assets/img/default.jpg" /><media:content medium="image" url="https://gilsondev.github.io/assets/img/default.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Automatizando seu fluxo de trabalho com Airflow</title><link href="https://gilsondev.github.io/airflow/dados/2018/06/23/automatizando-fluxo-de-trabalho-com-airflow/" rel="alternate" type="text/html" title="Automatizando seu fluxo de trabalho com Airflow" /><published>2018-06-23T00:00:00+00:00</published><updated>2018-06-23T00:00:00+00:00</updated><id>https://gilsondev.github.io/airflow/dados/2018/06/23/automatizando-fluxo-de-trabalho-com-airflow</id><content type="html" xml:base="https://gilsondev.github.io/airflow/dados/2018/06/23/automatizando-fluxo-de-trabalho-com-airflow/"><![CDATA[<p>É comum surgir situações em que precisamos fazer alguns procedimentos repetitivos em diversos projetos, principalmente quando envolve dados.</p>

<p>Um exemplo disso:</p>

<ul>
  <li>Migrar dados de uma tabela para outra</li>
  <li>Importar dados a partir de várias fontes e juntar em uma única base</li>
  <li>Seguir o passo acima e trabalhar com tratamento e filtragem das
informações</li>
</ul>

<p>Entre várias outras necessidade, mas no final acabamos tendo algo em comum que é definir um workflow, em que será composto por várias etapas para alcançarmos um objetivo, seja unificar, seja tratar, etc. Nesse post vamos falar do Apache Airflow, que é uma ferramenta opensource e que nos auxilia nesses tipos de situações.</p>

<h2 id="o-que-é-o-airflow">O que é o Airflow?</h2>

<p>Ela foi criada pelo time de desenvolvedores do AirBnB, com o intuito de definir fluxos de trabalho que envolve consulta de dados de inúmeras fontes, tratamento e mineração de dados, de forma que possa ser feito de forma periódica ou não, ou seja, uma pipeline de dados. Ela se tornou open source em meados de 2015, sendo divulgado por um <a href="https://medium.com/airbnb-engineering/airflow-a-workflow-management-platform-46318b977fd8">post</a> no blog aonde os engenheiros e cientistas de dados da empresa compartilham suas experiências. Depois de algum tempo, ela foi cedida para o Apache em que se tornou um dos inúmeros projetos que ela mantém, hoje chamado de <a href="https://github.com/apache/incubator-airflow">apache-airflow</a>.</p>

<h2 id="instalação-e-configuração-minima">Instalação e configuração minima</h2>

<p>Vamos preparar o ambiente e criar o primeiro workflow para entender como funciona. Primeiramente, vamos criar um ambiente virtual para instalarmos a ferramenta, então sugiro que use Python 3:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">mkdir </span>airflow-tutorial
<span class="nv">$ </span><span class="nb">cd </span>airflow-tutorial
<span class="nv">$ </span>python3 <span class="nt">-m</span> venv .venv
<span class="nv">$ </span><span class="nb">source</span> .venv/bin/activate
</code></pre></div></div>

<p>Instale o airflow usando <em>pip</em>:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>.venv<span class="o">)</span><span class="nv">$ </span>pip <span class="nb">install </span>apache-airflow
</code></pre></div></div>

<p>Conforme a <a href="https://airflow.apache.org/">documentação</a>, após a instalação é necessário definirmos a raiz para que possa ser gerado toda a base do airflow em que será usado.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Definindo o diretório airflow-tutorial como raiz do Airflow</span>
<span class="nv">$ </span><span class="nb">export </span><span class="nv">AIRFLOW_HOME</span><span class="o">=</span><span class="nv">$PWD</span>
</code></pre></div></div>

<p>Agora vamos gerar o ambiente com o comando abaixo.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>.venv<span class="o">)</span><span class="nv">$ </span>airflow initdb
</code></pre></div></div>

<p>Esse comando irá criar os arquivos necessários para o Airflow, e rodar as migrações em um banco SQLite, em que ele armazena os metadados dos workflows. Para uso local e didático, com esse tipo de banco é o suficiente, mas tem outros procedimentos para preparar o seu uso em produção. Outra observação: <strong>não se preocupe com o pedido do módulo cryptography.</strong> Isso não vai afetar na experiência, isso é importante quando precisa encriptar algumas informações que não é o caso levantar agora, mas o seu uso real é necessário.</p>

<p>Vamos criar uma pasta chamada <code class="language-plaintext highlighter-rouge">dags</code>, em que vamos armazenar os fluxos implementados. Mais na frente irei explicar melhor:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>.venv<span class="o">)</span><span class="nv">$ </span><span class="nb">mkdir </span>dags
</code></pre></div></div>

<p>Após preparar tudo, vamos iniciar o servidor local para vermos o painel administrativo da ferramenta.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>.venv<span class="o">)</span><span class="nv">$ </span>airflow webserver
</code></pre></div></div>

<p>Agora é acessar o <a href="http://localhost:8080">http://localhost:8080.</a> Feito isso você vai ver algo parecido com isso:</p>

<p><img src="https://gilsondev.github.io/assets/img/posts/introducao-airflow/airflow_home.png" alt="Página inicial do Painel Administrativo do Airflow" /></p>

<p>Nessa página ele lista todos os workflows instalados, e que nesse caso são os exemplos que vem com a ferramenta, em que mostra várias formas de construir a sua pipeline. O bacana de usar pela interface é que pode ter um controle das execuções e o histórico de cada um deles, como qual etapa do fluxo falhou acompanhado de um log de execução, mas essa não é a única forma de usar, temos também a opção de linha de comando que veremos em breve nesse post.</p>

<h2 id="criando-nosso-primeiro-workflow">Criando nosso primeiro workflow</h2>

<p>Agora com tudo pronto, vamos criar nosso exemplo. Vamos supor que precisamos fazer o fluxo abaixo:</p>

<ol>
  <li>Imprimir a data na saída padrão</li>
  <li>Definir um sleep de 5 segundos</li>
  <li>Salvar a data em um arquivo texto</li>
</ol>

<p><img src="https://gilsondev.github.io/assets/img/posts/introducao-airflow/fluxo-tutorial.png" alt="Um pequeno diagrama exemplificando o fluxo" /></p>

<p>A idéia é que ele seja executado diariamente, e que não seja cíclico. Outro ponto importante, é que a próxima etapa será executada se a anterior teve êxito no que foi criada a fazer após o seu processamento.</p>

<h2 id="explicando-a-estrutura-de-uma-dag">Explicando a estrutura de uma DAG</h2>

<p>Essa estrutura que preparamos, para o Airflow é chamada de <a href="https://airflow.apache.org/concepts.html#dags">DAG</a>:</p>

<blockquote>
  <p>DAG = Directed Acyclic Graph</p>
</blockquote>

<p>É um grafo acíclico, em que todas as tarefas que são executadas e organizadas de uma forma que reflete os seus relacionamentos e dependências. Você pode definir que um determinado nó deve ser executado se a anterior for um sucesso ou não, ou que o nó A terá um timeout de 5 minutos, e o nó B pode ser reiniciado até 5 vezes caso não tenha sucesso.</p>

<p>Dessa forma cada arquivo python armazena uma DAG, e ela é reconhecida pelo Airflow para sua execução na pasta <code class="language-plaintext highlighter-rouge">dags</code> que criamos anteriormente. Você <a href="https://airflow.apache.org/configuration.html#setting-configuration-options">pode mudar o caminho</a> dessa pasta, alterando as configurações da ferramenta.</p>

<h2 id="vamos-ao-código">Vamos ao código</h2>

<p>Explicado como é um workflow para o Airflor, vamos criar a DAG do nosso exemplo.</p>

<h3 id="preparando-a-dag">Preparando a DAG</h3>

<p>Crie um novo arquivo chamado de <code class="language-plaintext highlighter-rouge">fluxo_simples.py</code> na pasta <code class="language-plaintext highlighter-rouge">dags</code> e implemente o código abaixo:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">airflow</span>

<span class="kn">from</span> <span class="nn">airflow</span> <span class="kn">import</span> <span class="n">DAG</span>
<span class="kn">from</span> <span class="nn">airflow.operators.bash_operator</span> <span class="kn">import</span> <span class="n">BashOperator</span>
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span><span class="p">,</span> <span class="n">timedelta</span>

<span class="n">args</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">'owner'</span><span class="p">:</span> <span class="s">'airflow'</span><span class="p">,</span>
    <span class="s">'start_date'</span><span class="p">:</span> <span class="n">airflow</span><span class="p">.</span><span class="n">utils</span><span class="p">.</span><span class="n">dates</span><span class="p">.</span><span class="n">days_ago</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="p">}</span>

<span class="n">dag</span> <span class="o">=</span> <span class="n">DAG</span><span class="p">(</span>
    <span class="n">dag_id</span><span class="o">=</span><span class="s">'fluxo_simples'</span><span class="p">,</span>
    <span class="n">default_args</span><span class="o">=</span><span class="n">args</span><span class="p">,</span>
    <span class="n">schedule_interval</span><span class="o">=</span><span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">1</span><span class="p">),</span>
    <span class="n">dagrun_timeout</span><span class="o">=</span><span class="n">timedelta</span><span class="p">(</span><span class="n">minutes</span><span class="o">=</span><span class="mi">60</span><span class="p">)</span>
<span class="p">)</span>
</code></pre></div></div>

<p>O que fizemos aqui é preparar a criação da DAG em que ela possui algumas definições iniciais:</p>

<ul>
  <li>Possui um ID único chamado de <em>fluxo_simples</em>: <code class="language-plaintext highlighter-rouge">dag_id</code></li>
  <li>Recebe argumentos padrão e que reflete nos nós do grafo: <code class="language-plaintext highlighter-rouge">defaul_args</code></li>
  <li>Define a periodicidade de execução. Nesse caso é diária: <code class="language-plaintext highlighter-rouge">schedule_interval</code></li>
  <li>Define o tempo em que a DAG terá antes de ser interrompida: <code class="language-plaintext highlighter-rouge">dagrun_timeout</code></li>
</ul>

<p>A variável <code class="language-plaintext highlighter-rouge">dag</code> será necessária para vincular os nós que vamos criar.</p>

<h3 id="criando-os-nós-do-fluxo">Criando os nós do fluxo</h3>

<p>Continuando, no mesmo arquivo vamos criar os nós, baseado na estrutura que definirmos no exemplo:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># (...)
# Depois da preparação da DAG
</span>
<span class="c1"># 1. Imprime a data na saída padrão
</span><span class="n">task1</span> <span class="o">=</span> <span class="n">BashOperator</span><span class="p">(</span>
    <span class="n">task_id</span><span class="o">=</span><span class="s">'print_date'</span><span class="p">,</span>
    <span class="n">bash_command</span><span class="o">=</span><span class="s">'date'</span><span class="p">,</span>
    <span class="n">dag</span><span class="o">=</span><span class="n">dag</span><span class="p">)</span>

<span class="c1"># 2. Faz uma sleep de 5 segundos.
# Dando errado tente em no máximo 3 vezes
</span><span class="n">task2</span> <span class="o">=</span> <span class="n">BashOperator</span><span class="p">(</span>
    <span class="n">task_id</span><span class="o">=</span><span class="s">'sleep'</span><span class="p">,</span>
    <span class="n">bash_command</span><span class="o">=</span><span class="s">'sleep 5'</span><span class="p">,</span>
    <span class="n">retries</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
    <span class="n">dag</span><span class="o">=</span><span class="n">dag</span><span class="p">)</span>

<span class="c1"># 3. Salve a data em um arquivo texto
</span><span class="n">task3</span> <span class="o">=</span> <span class="n">BashOperator</span><span class="p">(</span>
    <span class="n">task_id</span><span class="o">=</span><span class="s">'save_date'</span><span class="p">,</span>
    <span class="n">bash_command</span><span class="o">=</span><span class="s">'date &gt; /tmp/date_output.txt'</span><span class="p">,</span>
    <span class="n">retries</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
    <span class="n">dag</span><span class="o">=</span><span class="n">dag</span><span class="p">)</span>
</code></pre></div></div>

<p>Com isso temos a DAG pronta. Criamos três tarefas em que cada uma delas é representado por um nó do nosso grafo e são criadas a partir de <code class="language-plaintext highlighter-rouge">Operators</code>, que são classes Python que oferece variados tipos de processamento. Aqui estamos usando do tipo <code class="language-plaintext highlighter-rouge">BashOperator</code> em que iremos executar comandos bash no console, passados no argumento <code class="language-plaintext highlighter-rouge">bash_command</code>. Cada tarefa criada também tem um ID único, então caso crie outro com o mesmo identificador, o Airflow vai gerar um erro antes de executá-la. Além disso, vinculamos as tarefas a dag criada através do argumento <code class="language-plaintext highlighter-rouge">dag</code>.</p>

<p>Por fim, precisamos definir a interligação dos nós e sua ordem de execução:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Interligando task1 ao task2
</span><span class="n">task1</span><span class="p">.</span><span class="n">set_downstream</span><span class="p">(</span><span class="n">task2</span><span class="p">)</span>

<span class="c1"># Interligando task2 ao task3
</span><span class="n">task2</span><span class="p">.</span><span class="n">set_downstream</span><span class="p">(</span><span class="n">task3</span><span class="p">)</span>
</code></pre></div></div>

<p>Cada tarefa instanciada possui métodos para fazer ligação entre eles. No exemplo acima, usamos o <code class="language-plaintext highlighter-rouge">set_downstream</code> para definir que será a próxima tarefa a interligar no nó em questão. Temos também o <code class="language-plaintext highlighter-rouge">set_upstream</code> que define quem será o nó anterior. Segue a imagem para ficar mais claro a ligação:</p>

<p><img src="https://gilsondev.github.io/assets/img/posts/introducao-airflow/fluxo-tutorial-2.png" alt="Um pequeno diagrama exemplificando o fluxo" /></p>

<p>Os operators são importantes para a definição das DAGS. Abaixo segue alguns que a ferramenta disponibiliza:</p>

<ul>
  <li>BashOperator: executa comandos shellscript</li>
  <li>DummyOperator: cria um nó que não executa nada, somente para representação</li>
  <li>EmailOperator: envia e-mail</li>
  <li>HdfsOperator: aguarda por um arquivo ou pasta no HDFS</li>
  <li>HiveOperator: executa código <em>hql</em> em um banco de dados Hive específico</li>
  <li>SimpleHttpOperator: faz uma requisição de um endpoint HTTP</li>
  <li>MySqlOperator: executa códigos SQL em um banco MySQL</li>
  <li>PostgresOperator: executa códigos SQL em um banco PostgreSQL</li>
  <li>PythonOperator: executa um código callable em Python</li>
</ul>

<p>Isso são alguns dos <a href="https://airflow.apache.org/code.html#operators">vários operators</a> disponíveis, como existem outros <a href="https://github.com/airflow-plugins">operators</a> criados e mantidos pela comunidade.</p>

<h3 id="agora-é-executar">Agora é executar</h3>

<p>Com a nossa DAG totalmente finalizada, vamos testar executando na linha de comando:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>.venv<span class="o">)</span><span class="nv">$ </span>airflow backfill fluxo_simples <span class="nt">-s</span> 2018-06-09
</code></pre></div></div>

<p>Com isso, através do identificador que passamos via parâmetro ele irá localizar a DAG e executar todo o nosso fluxo. A data é importante para saber a data inicial da execução. Você vai ver várias saídas no console, como qual a tarefa que está alocada no momento para executar, o seu processamento e saída gerada, entre outras informações que consegue acessar também pelo Painel Administrativo. Abaixo vou colocar alguns trechos que faz sentido para o nosso tutorial:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>2018-06-09 12:14:36,996] <span class="o">{</span>base_task_runner.py:98<span class="o">}</span> INFO - Subtask: <span class="o">[</span>2018-06-09 12:14:36,996] <span class="o">{</span>bash_operator.py:97<span class="o">}</span> INFO - Output:
<span class="o">[</span>2018-06-09 12:14:36,997] <span class="o">{</span>base_task_runner.py:98<span class="o">}</span> INFO - Subtask: <span class="o">[</span>2018-06-09 12:14:36,997] <span class="o">{</span>bash_operator.py:101<span class="o">}</span> INFO - Sáb Jun 09 12:14:36 <span class="nt">-03</span> 2018
</code></pre></div></div>

<p>Aqui ele executa o comando <code class="language-plaintext highlighter-rouge">date</code> e traz o resultado para a saída padrão.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>2018-06-09 12:14:53,475] <span class="o">{</span>base_task_runner.py:98<span class="o">}</span> INFO - Subtask: <span class="o">[</span>2018-06-09 12:14:53,475] <span class="o">{</span>bash_operator.py:88<span class="o">}</span> INFO - Running <span class="nb">command</span>: <span class="nb">date</span> <span class="o">&gt;</span> /tmp/date_output.txt
<span class="o">[</span>2018-06-09 12:14:53,477] <span class="o">{</span>base_task_runner.py:98<span class="o">}</span> INFO - Subtask: <span class="o">[</span>2018-06-09 12:14:53,477] <span class="o">{</span>bash_operator.py:97<span class="o">}</span> INFO - Output:
<span class="o">[</span>2018-06-09 12:14:53,479] <span class="o">{</span>base_task_runner.py:98<span class="o">}</span> INFO - Subtask: <span class="o">[</span>2018-06-09 12:14:53,479] <span class="o">{</span>bash_operator.py:105<span class="o">}</span> INFO - Command exited with <span class="k">return </span>code 0
</code></pre></div></div>

<p>E por aqui mostra que a execução do <code class="language-plaintext highlighter-rouge">date</code> e o resultado salvo em um arquivo foi feito com sucesso. Se for fazer um <code class="language-plaintext highlighter-rouge">cat</code> no arquivo gerado vai encontrar algo parecido:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>.venv<span class="o">)</span><span class="nv">$ </span><span class="nb">cat</span> /tmp/date_output.txt
Sáb Jun 09 12:14:53 <span class="nt">-03</span> 2018
</code></pre></div></div>

<h2 id="alguns-comandos-interessantes">Alguns comandos interessantes</h2>

<p>Outros comandos que pode acabar usando na linha de comando:</p>

<p>Disparar a execução de uma DAG para que possa <a href="https://airflow.apache.org/scheduler.html">preparar o agendamento</a>, caso isso esteja habilitado:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>airflow trigger_dag fluxo_simples <span class="nt">-e</span> 2018-06-10
</code></pre></div></div>

<p>Testar uma tarefa específica, ao invés de toda a DAG:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>airflow <span class="nb">test </span>fluxo_simples print_date 2018-06-09
</code></pre></div></div>

<p>Nesse caso ele se basea em alguma configuração específica para testes no arquivo <code class="language-plaintext highlighter-rouge">unittest.cfg</code> e que não precisa usar alguma configuração no <code class="language-plaintext highlighter-rouge">airflow.cfg</code>.</p>

<p>Caso queira saber o status da execução de uma DAG:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>airflow dag_state fluxo_simples 2018-06-09
</code></pre></div></div>

<p>Executar uma tarefa específica, ao invés de toda a DAG:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>airflow run fluxo_simples print_date 2018-06-09
</code></pre></div></div>

<p>Listar todas as tarefas de uma DAG:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>airflow list_tasks fluxo_simples
</code></pre></div></div>

<p>Listar todas as DAGS disponíveis:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>airflow list_dags
</code></pre></div></div>

<p>Iniciar uma instância de scheduler. Isso vamos aprofundar quando abordarmos sobre os <code class="language-plaintext highlighter-rouge">Executors</code> do airflow. Aqui fica a título de curiosidade, ele é importante para verificar quais as próximas DAGS que estão perto de serem iniciadas a partir do que configurou na sua periodicidade:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>airflow scheduler
</code></pre></div></div>

<p>Limpar o conjunto de instâncias de tarefas de uma DAG, como se nunca tivesse sido executado:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>airflow clear fluxo_simples
</code></pre></div></div>

<h2 id="visualizando-no-painel">Visualizando no Painel</h2>

<p>Agora vamos verificar como conseguimos acessar as informações da nossa DAG no Painel Administrativo. Com o <code class="language-plaintext highlighter-rouge">airflow webserver</code> executado, você deverá visualizar algo parecido das imagens abaixo:</p>

<ol>
  <li>Página inicial do Painel, contendo a lista das dags e a nossa no final. Ela mostra a quantidade de vezes que teve sucesso e falha na DAG inteira, como na quantidade de tarefas.</li>
</ol>

<p><img src="https://gilsondev.github.io/assets/img/posts/introducao-airflow/painel1.png" alt="Página inicial com informações da nossa DAG" /></p>

<ol>
  <li>Podemos ver abaixo a linha com a nossa DAG. Quando executei estava com o nome <code class="language-plaintext highlighter-rouge">tutorial</code>, mas não é nada diferente do que fizemos nesse post:</li>
</ol>

<p><img src="https://gilsondev.github.io/assets/img/posts/introducao-airflow/painel2.png" alt="Linha com as informações da nossa DAG" /></p>

<p><img src="https://gilsondev.github.io/assets/img/posts/introducao-airflow/painel3.png" alt="Linha com as informações da nossa DAG, com tamanho maior" /></p>

<p>Explicando cada tipo de estado e a quantidade em que cada DAG e tarefa o Airflow registrou:</p>

<p><img src="https://gilsondev.github.io/assets/img/posts/introducao-airflow/detalhes-painel1.png" alt="Explicando cada estado" /></p>

<ol>
  <li>Clicando no nome da DAG, temos mais detalhes dela, como diferentes formas de visualizar todo os metadados gerados durante seu processamento.</li>
</ol>

<p><strong>Visão em árvore</strong>
<img src="https://gilsondev.github.io/assets/img/posts/introducao-airflow/painel-treeview.png" alt="Visão hierárquica" /></p>

<p><strong>Visão em grafo</strong>
<img src="https://gilsondev.github.io/assets/img/posts/introducao-airflow/painel-graphview.png" alt="Visão em grafo" /></p>

<p><strong>Visão por tarefa e sua duração</strong>
<img src="https://gilsondev.github.io/assets/img/posts/introducao-airflow/painel-taskduration.png" alt="Visão da duração da tarefa" /></p>

<p><strong>Visão em Gantt da execução das tarefas</strong>
<img src="https://gilsondev.github.io/assets/img/posts/introducao-airflow/painel-gantt.png" alt="Visão em Gantt da duração da tarefa" /></p>

<p><strong>Visão do código da DAG</strong>
<img src="https://gilsondev.github.io/assets/img/posts/introducao-airflow/painel-codeview.png" alt="Visão do código" /></p>

<h2 id="e-agora">E agora?</h2>

<p>Esse post procurou trazer uma visão geral do Airflow, e o potencial que ele tem para auxiliar em automatização de fluxos de trabalho em geral. Segue alguns projetos que desenvolvi e que pode ser bons exemplo de como usar a ferramenta. Então façam o fork e testem a vontade para entender mais.</p>

<div class="github-card" data-github="gilsondev/dag_dominios_govbr" data-width="800" data-height="130" data-theme="default"></div>
<div class="github-card" data-github="meucandidato/airflow-dags" data-width="800" data-height="130" data-theme="default"></div>

<p>Espero que tenham gostado. Quaisquer dúvidas e sugestões comentem embaixo e vamos trocar idéias.</p>

<p>Até mais!</p>]]></content><author><name>Gilson Filho</name><email>me@gilsondev.in</email></author><category term="airflow" /><category term="dados" /><category term="python" /><category term="airflow" /><category term="workflow" /><category term="datamining" /><category term="etl" /><summary type="html"><![CDATA[É comum surgir situações em que precisamos fazer alguns procedimentos repetitivos em diversos projetos, principalmente quando envolve dados.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://gilsondev.github.io/assets/img/default.jpg" /><media:content medium="image" url="https://gilsondev.github.io/assets/img/default.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Palestra sobre Sentry no FLISOL-DF 2018</title><link href="https://gilsondev.github.io/django/2018/04/28/palestra-sentry/" rel="alternate" type="text/html" title="Palestra sobre Sentry no FLISOL-DF 2018" /><published>2018-04-28T00:00:00+00:00</published><updated>2018-04-28T00:00:00+00:00</updated><id>https://gilsondev.github.io/django/2018/04/28/palestra-sentry</id><content type="html" xml:base="https://gilsondev.github.io/django/2018/04/28/palestra-sentry/"><![CDATA[<p>No FLISOL-DF 2018 fui convidado para palestrar, e procurei abordar
sobre uma ferramenta de monitoramento chamado Sentry, e como ele pode ajudar o time ao resolver os problemas que ocorrem em produção, muitas das vezes antes de aparecer ao usuário.</p>

<p>Fiquei surpreso que muita gente não conhece, então preparei um material abordando sobre ela e como pode preparar para usar em variados projetos. Segue mais nos slides abaixo:</p>

<p><br /></p>

<script async="" class="speakerdeck-embed" data-id="0d5e9e25103149359c4a27af4d7eed84" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script>

<p>Para demonstração criei três projetos com tecnologias distintas:</p>

<ul>
  <li>Python: usando o framework web Django</li>
  <li>PHP: usando o framework web Laravel</li>
  <li>Java: usando o framework web Spring Boot</li>
</ul>

<p>Abaixo segue o link do repositório com os projetos de exemplo:</p>

<div class="github-card" data-github="gilsondev/sentry-demo" data-width="800" data-height="130" data-theme="default"></div>
<script src="//cdn.jsdelivr.net/github-cards/latest/widget.js"></script>

<p>Quem quiser saber mais da ferramenta, comenta embaixo que podemos preparar um webinário sobre. O que acham?</p>

<p>Até mais!</p>]]></content><author><name>Gilson Filho</name><email>me@gilsondev.in</email></author><category term="django" /><category term="django" /><category term="rest" /><summary type="html"><![CDATA[No FLISOL-DF 2018 fui convidado para palestrar, e procurei abordar sobre uma ferramenta de monitoramento chamado Sentry, e como ele pode ajudar o time ao resolver os problemas que ocorrem em produção, muitas das vezes antes de aparecer ao usuário.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://gilsondev.github.io/assets/img/posts/palestra-sentry/cover.png" /><media:content medium="image" url="https://gilsondev.github.io/assets/img/posts/palestra-sentry/cover.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Meu primeiro webinário</title><link href="https://gilsondev.github.io/git/2017/05/25/primeiro-webinario/" rel="alternate" type="text/html" title="Meu primeiro webinário" /><published>2017-05-25T00:00:00+00:00</published><updated>2017-05-25T00:00:00+00:00</updated><id>https://gilsondev.github.io/git/2017/05/25/primeiro-webinario</id><content type="html" xml:base="https://gilsondev.github.io/git/2017/05/25/primeiro-webinario/"><![CDATA[<p>Olá pessoal, No dia 24 de Maio, iniciei o meu primeiro webinário, focado
em ensinar o básico do controle de versão Git. A iniciativa começou
diante da necessidade de entender melhor a ferramenta pelos colegas do
curso <a href="http://welcometothedjango.com.br/">Welcome to the Django</a>.</p>

<p>Dessa forma, fiz uma live no Youtube abordando o que é o Git, mostrando o que seria um controle de versão descentralizada, e os comandos mais usados durante o desenvolvimento com ele. Então, fica abaixo o vídeo do
webinário:</p>

<p><br /></p>
<iframe title="YouTube video player" width="800" height="390" src="//www.youtube.com/embed/ubYaLJDWhSY" frameborder="0" allowfullscreen=""></iframe>
<p><br /></p>

<p>Procurei abordar tudo que planejei, mas depois de 2 horas precisei pular
alguns tópicos como
<a href="http://nvie.com/posts/a-successful-git-branching-model/">Gitflow</a> e
outros comandos mais avançados (como bisect, rebase, etc), mas
infelizmente o tempo que tinha planejado estava estourando. Por isso
mais tarde irei fazer outros webinários com esses temas.</p>

<p>Até a próxima LIVE ;)</p>]]></content><author><name>Gilson Filho</name><email>me@gilsondev.in</email></author><category term="git" /><category term="git" /><category term="webinario" /><summary type="html"><![CDATA[Olá pessoal, No dia 24 de Maio, iniciei o meu primeiro webinário, focado em ensinar o básico do controle de versão Git. A iniciativa começou diante da necessidade de entender melhor a ferramenta pelos colegas do curso Welcome to the Django.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://gilsondev.github.io/assets/img/default.jpg" /><media:content medium="image" url="https://gilsondev.github.io/assets/img/default.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Visualizando migrations dependentes com django-migrations-graph</title><link href="https://gilsondev.github.io/django/2017/05/22/visualizando-migrations-dependentes-com-django-migrations-graph/" rel="alternate" type="text/html" title="Visualizando migrations dependentes com django-migrations-graph" /><published>2017-05-22T00:00:00+00:00</published><updated>2017-05-22T00:00:00+00:00</updated><id>https://gilsondev.github.io/django/2017/05/22/visualizando-migrations-dependentes-com-django-migrations-graph</id><content type="html" xml:base="https://gilsondev.github.io/django/2017/05/22/visualizando-migrations-dependentes-com-django-migrations-graph/"><![CDATA[<p>Quando trabalhos em projetos Django, a partir do momento em que ele sofre inúmeras mudanças, acabamos usando as migrations para que o banco possa refletir as alterações. Com o tempo, a quantidade delas aumenta, e não sabemos exatamente qual módulo de migração depende de outra. Para resolver esse problema, foi criado o <a href="https://github.com/dizballanze/django-migrations-graph">django-migrations-graph</a>.</p>

<p>O objetivo ele é dar uma opção via management command, para que ele acesse as migrações e defina no terminal as dependências entre elas. Então vamos começar a usar.</p>

<h2 id="instalação">Instalação</h2>

<p>Antes de mais nada, vamos preparar um ambiente virtualenv e criar um projeto de exemplo:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python3 <span class="nt">-m</span> venv .venv
<span class="nv">$ </span>pip <span class="nb">install </span>django
<span class="nv">$ </span>django-admin startproject graph_example <span class="nb">.</span>
</code></pre></div></div>

<p>Depois, vamos instalar o pacote e inserir o aplicativo no INSTALLED_APPS do projeto:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>pip <span class="nb">install </span>django-migrations-graph
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">INSTALLED_APPS</span> <span class="o">=</span> <span class="p">[</span>
    <span class="s">'django.contrib.admin'</span><span class="p">,</span>
    <span class="s">'django.contrib.auth'</span><span class="p">,</span>
    <span class="s">'django.contrib.contenttypes'</span><span class="p">,</span>
    <span class="s">'django.contrib.sessions'</span><span class="p">,</span>
    <span class="s">'django.contrib.messages'</span><span class="p">,</span>
    <span class="s">'django.contrib.staticfiles'</span><span class="p">,</span>

    <span class="c1"># Third Apps
</span>    <span class="s">'migraph'</span><span class="p">,</span>
<span class="p">]</span>
</code></pre></div></div>

<p>Instalado e configurado, vamos aproveitar os apps contrib para verificar a dependência entre <em>contrib.auth</em> e <em>contrib.admin</em>:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python manage.py migration_dependencies auth admin
<span class="o">[</span>auth]
auth/0001_initial
        Depending:
                admin/0001_initial
        Depends on:
                contenttypes/__first__
auth/0002_alter_permission_name_max_length
auth/0003_alter_user_email_max_length
auth/0004_alter_user_username_opts
auth/0005_alter_user_last_login_null
auth/0006_require_contenttypes_0002
        Depends on:
                contenttypes/0002_remove_content_type_name
auth/0007_alter_validators_add_error_messages
auth/0008_alter_user_username_max_length

<span class="o">[</span>admin]
admin/0001_initial
        Depends on:
                auth/__first__
                contenttypes/__first__
admin/0002_logentry_remove_auto_add
</code></pre></div></div>

<p>É muito interessante! Vendo a saída, ao passarmos os aplicativos para efetuar a introspecção das migrações, ele mostra acima que o auth/0001<em>initial depente do 0001_initial do _contrib.admin</em>, e que este depende de vários outros do <em>contrib.contenttypes</em>. Vemos também que em cada app ele separa por <em>[nome do app]</em>.</p>

<p>Esse app vai ajudar muito quando precisa trabalhar em projetos que ainda não conhece como um todo, aprender mais das migrações, como foram feitas e suas dependências.</p>

<p>Espero que tenham gostado. Até mais!</p>]]></content><author><name>Gilson Filho</name><email>me@gilsondev.in</email></author><category term="django" /><category term="django" /><category term="migrations" /><category term="python" /><summary type="html"><![CDATA[Quando trabalhos em projetos Django, a partir do momento em que ele sofre inúmeras mudanças, acabamos usando as migrations para que o banco possa refletir as alterações. Com o tempo, a quantidade delas aumenta, e não sabemos exatamente qual módulo de migração depende de outra. Para resolver esse problema, foi criado o django-migrations-graph.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://gilsondev.github.io/assets/img/default.jpg" /><media:content medium="image" url="https://gilsondev.github.io/assets/img/default.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>