<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Gilson #DEV]]></title><description><![CDATA[Gilson #DEV]]></description><link>https://blog.gilsondev.in</link><generator>RSS for Node</generator><lastBuildDate>Fri, 17 Apr 2026 12:27:43 GMT</lastBuildDate><atom:link href="https://blog.gilsondev.in/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Kubernetes - Uma visão geral da famosa ferramenta de orquestração de contêineres]]></title><description><![CDATA[Olá pessoal,
Nesse post quero trazer uma visão geral do Kubernetes, sua história, e como sua arquitetura é composta. Mas o que é o Kubernetes?
Ele é uma plataforma opensource portável, extensível focado em gerenciar serviços containerizados, desenvol...]]></description><link>https://blog.gilsondev.in/kubernetes-uma-visao-geral-da-famosa-ferramenta-de-orquestracao-de-conteineres</link><guid isPermaLink="true">https://blog.gilsondev.in/kubernetes-uma-visao-geral-da-famosa-ferramenta-de-orquestracao-de-conteineres</guid><category><![CDATA[Devops]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Kubernetes]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Mon, 20 Mar 2023 13:00:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/1cqIcrWFQBI/upload/e0de5200c633f0e188d841af6130cc67.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Olá pessoal,</p>
<p>Nesse post quero trazer uma visão geral do Kubernetes, sua história, e como sua arquitetura é composta. Mas o que é o Kubernetes?</p>
<p>Ele é uma plataforma opensource portável, extensível focado em gerenciar serviços containerizados, desenvolvida pela Google em 2014 e lançada publicamente em 2015. Essa plataforma atua de forma que ele ajuda a preparar e gerenciar clusters, e também nos contâiners Docker e portanto atua na sua <strong>orquestração</strong> e isso é possível através da automação por meio de configurações declarativas via arquivos YAML. Com isso ela oferece uma alta disponibilidade, escalabilidade e resiliência em ambientes de produção.</p>
<h2 id="heading-como-ela-surgiu">Como ela surgiu?</h2>
<p>O surgiu a partir de uma tecnologia de orquestração de contêineres interna chamada <a target="_blank" href="https://research.google/pubs/pub43438/">Borg</a>. A Google começou a trabalhar nessa ferramenta em 2003 para gerenciar seus próprios serviços internos em contêineres. Ele foi projetado para lidar com a escalabilidade e a alta disponibilidade de serviços, além de oferecer um ambiente de desenvolvimento mais fácil e ágil.</p>
<p>Em 2014, a Google decidiu disponibilizar o que conhecemos como Kubernetes hoje, mas como um projeto de código aberto, o que permitiu que outras empresas e desenvolvedores utilizassem a tecnologia. O projeto rapidamente se tornou popular, atraindo uma comunidade de desenvolvedores e contribuidores em todo o mundo. Em 2015, a Kubernetes foi doada para a Cloud Native Computing Foundation (CNCF), uma organização de código aberto que ajuda a gerenciar e promover tecnologias nativas de nuvem.</p>
<p>Desde então, ela tornou-se uma das tecnologias mais populares no espaço de contêineres, sendo amplamente adotada por empresas de todos os tamanhos em todo o mundo.</p>
<h2 id="heading-como-era-antes">Como era antes?</h2>
<p>Antes do Kubernetes, os deploys eram geralmente feitos de forma manual, o que exigia muito esforço. Os desenvolvedores precisavam configurar manualmente cada servidor e instalar todas as dependências e bibliotecas necessárias. Isso exigia muitos scripts de automação personalizados e era um processo propenso a erros humanos.</p>
<p>Olhando em uma perspectiva maior, a imagem mostra como tem sido a evolução da infraestrutura em que as nossas aplicações eram implantados:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677555299790/8da00b5e-427e-4b1c-b8b8-291abd83cb2a.png" alt="antes do kubernetes" class="image--center mx-auto" /></p>
<ul>
<li><p><strong>Deploy tradicional</strong>: Nesse momento era feito o deploy em máquinas físicas, e com isso um ou mais aplicações utilizam o mesmo recurso. O problema disso é que uma determinada aplicação poderia utilizar recursos o suficiente para deixar quaisquer outras subutilizadas a nível de performance. Então para resolver isso era separando sua implantação em máquinas físicas diferentes, tornando seu gerenciamento e manutenção custosos e caros.</p>
</li>
<li><p><strong>Deploy virtualizado</strong>: Como solução, a virtualização foi introduzido, em que temos um ou mais servidores que atuam como <em>hypervisor</em> e assim eles hospedam ou mais máquinas virtuais. Dessa forma as aplicações estão isoladas a nível desta virtualização, cada um com seus recursos delimitados desde o início. Outra vantagem é que a escalabilidade vertical aqui nesse ambiente é instântanea porque não é acoplada com o físico totalmente, mas claro ainda é limitado.</p>
</li>
<li><p><strong>Deploy via contâiner</strong>: Contâiners são similares aos VMs, mas eles possuem um isolamento menos estrito, de forma que compartilham do mesmo Sistema Operacional. Dessa forma, além de serem leves por isso como VMs eles possuem o seu próprio <em>filesystem</em>, compartilhamento de CPU, memória, <em>process space</em>, e muito mais. Como eles são mais desacoplados da infraestrutura, eles são muito mais portáveis entre clouds e outros sistemas operacionais.</p>
</li>
</ul>
<p>O gerenciamento de aplicativos também era um desafio. À medida que o número de servidores aumentava, era difícil garantir que cada servidor estivesse executando as mesmas versões de software e bibliotecas. Também era difícil escalar aplicativos para atender às necessidades de tráfego crescente. Para lidar com esses problemas, surgiram ferramentas como Ansible, Chef e Puppet, que automatizavam a configuração do servidor e gerenciavam as dependências do aplicativo.</p>
<p>No entanto, essas ferramentas não eram específicas para contêineres, o que significava que os desenvolvedores ainda precisavam gerenciar manualmente as implantações e escalas dos contêineres. Isso levou ao desenvolvimento de ferramentas de orquestração de contêineres, como o Rancher, Docker Swarm e o Apache Mesos. No entanto, essas ferramentas tinham suas próprias limitações. Por exemplo, o Docker Swarm não tinha recursos avançados de escalabilidade e gerenciamento de aplicativos, enquanto o Apache Mesos era mais complexo de configurar e gerenciar.</p>
<p>Foi nesse contexto que o Kubernetes foi desenvolvido, para preencher essa lacuna e oferecer uma plataforma de orquestração de contêineres de ponta a ponta.</p>
<h2 id="heading-como-e-sua-arquitetura">Como é sua arquitetura?</h2>
<p>Ele tem uma arquitetura altamente modular e escalável, que consiste em vários componentes que trabalham juntos para fornecer recursos de orquestração de contêineres. Esses componentes incluem o seguinte:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1677555321119/e5f02250-baad-443d-9ef1-705fdd5b071c.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-cluster">Cluster</h3>
<p>O cluster é a estrutura básica do Kubernetes, que consiste em um ou mais nós (ou servidores) em um grupo. Cada nó executa um agente de Kubernetes (kubelet) para gerenciar os contêineres e outros recursos. O cluster é gerenciado pelo componente de controle do Kubernetes.</p>
<h3 id="heading-componente-de-controle">Componente de controle</h3>
<p>O componente de controle do Kubernetes é responsável por gerenciar o estado do cluster. Ele é composto por vários componentes, incluindo:</p>
<ul>
<li><p><strong>kube-apiserver</strong>: é um componente que expõem um API Kubernetes e que serve de frontend para o <em>control pane</em>, ou seja, é aqui que vai receber os comandos do “mundo externo” através do programa <code>kubectl</code> de maneira <strong>declarativa</strong> ou <strong>imperativa</strong></p>
</li>
<li><p><strong>etcd</strong>: atua como storage de chave e valor para o Kubernetes, armazenando dados relativos aos clusters</p>
</li>
<li><p><strong>kube-scheduler</strong>: componente que observa por novas criações de <em>pods</em> que não possuem atribuição e nenhum <em>node</em> e seleciona um mais adequado para ser hospedado e executado</p>
</li>
<li><p><strong>kube-controller-manager</strong>: é um componente que execute processos controladores, o que basicamente são processos que rodam em <em>loop</em> para regular o estado do sistema. No <em>control pane</em> temos alguns tipos como <em>Node Controller</em>, <em>Job Controller</em>, entre outros.</p>
</li>
<li><p><strong>cloud-controller-manager</strong>: esse componente incorpora a lógica de controle do seu cluster com as APIs dos provedores da nuvem, separando as partes do cluster as particularidades dos fornecedores. Aqui ele executa controladores específicos do fornecedor que implantou seu ambiente. Dessa forma, caso esteja utilizando um cluster em sua própria infraestrutura ou está utilizando para fins de estudo, você não utiliza esse componente.</p>
</li>
</ul>
<h3 id="heading-nos">Nós</h3>
<p>Componentes dos nós executam em cada uma delas, mantendo a execução dos contêineres e provendo o ambiente de execução para o Kubernetes.</p>
<p>Cada um desses componentes abaixo rodam em cada nó do cluster, fazendo parte do conceito de <em>Kubernetes Service</em>.</p>
<ul>
<li><p><strong>kubelet</strong>: ele tem o objetivo de manter o conjunto de <em>pods</em>; registrar o nó ao cluster, enviando eventos e status relacionados além de reportar utilização de recursos. Também ele recebe um conjunto de <em>PodSpecs</em> por meio do API Kubernetes para garantir que os pods estarão executando conforme esperado e com estabilidade.</p>
</li>
<li><p><strong>kube-proxy</strong>: ele gerencia e mantêm as regras de rede do nó do cluster. É com essas regras que que autoriza a comunicação dos <em>pods</em> dentro ou fora do seu cluster.</p>
</li>
</ul>
<h3 id="heading-pods">Pods</h3>
<p>Os pods são a unidade básica de implantação no Kubernetes, que consiste em um ou mais contêineres que compartilham o mesmo espaço de rede e armazenamento. Os pods são agendados em nós pelos componentes de controle e são gerenciados pelo kubelet em cada nó.</p>
<h3 id="heading-servicos">Serviços</h3>
<p>Os serviços fornecem uma maneira de expor um conjunto de pods como um serviço de rede. Eles são responsáveis por encaminhar o tráfego para os pods correspondentes, permitindo que os aplicativos sejam escalonados e mantidos sem interrupções.</p>
<h3 id="heading-volumes">Volumes</h3>
<p>Os volumes fornecem uma maneira de armazenar dados persistentes em contêineres. Os volumes podem ser compartilhados entre vários contêineres em um pod e podem ser gerenciados pelo Kubernetes para garantir que os dados persistam durante as atualizações e falhas.</p>
<h2 id="heading-uma-ferramenta-cada-vez-mais-essencial-nas-solucoes">Uma ferramenta cada vez mais essencial nas soluções</h2>
<p>O Kubernetes é uma ferramenta poderosa e flexível gerenciarmos e mantermos nossas aplicações rodando. Com sua arquitetura altamente modular e escalável, ele permite que os desenvolvedores gerenciem e orquestrem facilmente suas aplicações em contêineres, independentemente da infraestrutura subjacente. Hoje faz parte da rotina de todo desenvolvedor trabalhar com essa infraestrutura, de forma direta ou não, seja <em>onpremise</em> ou no <em>cloud</em>. Caso não conheça, acesse a <a target="_blank" href="https://kubernetes.io/docs/home/">documentação</a> para saber mais.</p>
<p>Até mais!</p>
]]></content:encoded></item><item><title><![CDATA[Linguagem de Manipulação de Dados no BigQuery: Como Realizar Consultas SQL Avançadas]]></title><description><![CDATA[O BigQuery é um serviço de armazenamento e análise de dados em nuvem, que suporta diversas linguagens de manipulação de dados (DML) para realizar consultas SQL avançadas. Com isso, é possível executar consultas complexas em grandes volumes de dados c...]]></description><link>https://blog.gilsondev.in/linguagem-de-manipulacao-de-dados-no-bigquery-como-realizar-consultas-sql-avancadas</link><guid isPermaLink="true">https://blog.gilsondev.in/linguagem-de-manipulacao-de-dados-no-bigquery-como-realizar-consultas-sql-avancadas</guid><category><![CDATA[Databases]]></category><category><![CDATA[NoSQL]]></category><category><![CDATA[bigquery]]></category><category><![CDATA[google cloud]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Sat, 25 Feb 2023 01:08:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/pCqzMe04s8g/upload/83888a8f14e3a1fd5d6761835f4694b2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>O BigQuery é um serviço de armazenamento e análise de dados em nuvem, que suporta diversas linguagens de manipulação de dados (DML) para realizar consultas SQL avançadas. Com isso, é possível executar consultas complexas em grandes volumes de dados com rapidez e eficiência. Neste artigo, vamos discutir as principais linguagens de manipulação de dados suportadas pela ferramenta e como utilizá-las em suas consultas SQL.</p>
<h2 id="heading-clausula-select">Cláusula SELECT</h2>
<p>Ele é usada para selecionar dados de uma tabela. É possível selecionar colunas específicas, filtrar dados com base em condições e aplicar funções de agregação a valores numéricos.</p>
<p>Exemplo de consulta SQL utilizando a cláusula <code>SELECT</code>:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> customer_id, <span class="hljs-keyword">SUM</span>(price) <span class="hljs-keyword">as</span> total_price
<span class="hljs-keyword">FROM</span> orders
<span class="hljs-keyword">WHERE</span> order_date &gt;= <span class="hljs-string">'2022-01-01'</span>
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> customer_id;
</code></pre>
<p>Nessa consulta, a cláusula SELECT seleciona o ID do cliente e o total de gastos em pedidos de cada cliente desde o início de 2022. A cláusula GROUP BY é usada para agrupar os dados por ID do cliente, permitindo calcular o total de gastos de cada cliente.</p>
<h2 id="heading-clausula-insert">Cláusula INSERT</h2>
<p>A cláusula INSERT é usada para inserir novas linhas em uma tabela. É possível inserir dados em uma única linha ou em várias linhas em uma única consulta.</p>
<p>Exemplo simples de uma consulta SQL utilizando a cláusula <code>INSERT</code>:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> customers (<span class="hljs-keyword">id</span>, <span class="hljs-keyword">name</span>, email)
<span class="hljs-keyword">VALUES</span> (<span class="hljs-number">123</span>, <span class="hljs-string">'João Silva'</span>, <span class="hljs-string">'joao.silva@email.com'</span>);
</code></pre>
<p>Além disso, podemos utilizar o <code>INSERT</code> com outras cláusulas.</p>
<h4 id="heading-usando-valores-explicitos">Usando valores explícitos</h4>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> dataset.Inventory (product, quantity)
<span class="hljs-keyword">VALUES</span>  (<span class="hljs-string">'top load washer'</span>, <span class="hljs-number">10</span>),
                (<span class="hljs-string">'front load washer'</span>, <span class="hljs-number">20</span>),
                (<span class="hljs-string">'dryer'</span>, <span class="hljs-number">30</span>),
                (<span class="hljs-string">'refrigerator'</span>, <span class="hljs-number">10</span>),
                (<span class="hljs-string">'microwave'</span>, <span class="hljs-number">20</span>),
                (<span class="hljs-string">'dishwasher'</span>, <span class="hljs-number">30</span>),
                (<span class="hljs-string">'oven'</span>, <span class="hljs-number">5</span>)
</code></pre>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> dataset.NewArrivals (product, quantity, warehouse)
<span class="hljs-keyword">VALUES</span> (<span class="hljs-string">'top load washer'</span>, <span class="hljs-number">100</span>, <span class="hljs-string">'warehouse #1'</span>),
             (<span class="hljs-string">'dryer'</span>, <span class="hljs-number">200</span>, <span class="hljs-string">'warehouse #2'</span>),
             (<span class="hljs-string">'oven'</span>, <span class="hljs-number">300</span>, <span class="hljs-string">'warehouse #3'</span>)
</code></pre>
<h4 id="heading-utilizando-instrucao-insert-select">Utilizando instrução <code>INSERT SELECT</code></h4>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> dataset.Warehouse (warehouse, state)
<span class="hljs-keyword">SELECT</span> *
<span class="hljs-keyword">FROM</span> <span class="hljs-keyword">UNNEST</span>([(<span class="hljs-string">'warehouse #1'</span>, <span class="hljs-string">'WA'</span>),
                         (<span class="hljs-string">'warehouse #2'</span>, <span class="hljs-string">'CA'</span>),
                         (<span class="hljs-string">'warehouse #3'</span>, <span class="hljs-string">'WA'</span>)])
</code></pre>
<h4 id="heading-utilizando-instrucao-with-como-alternativa-ao-insert-select">Utilizando instrução <code>WITH</code> como alternativa ao <code>INSERT SELECT</code></h4>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> dataset.Warehouse (warehouse, state)
<span class="hljs-keyword">WITH</span> w <span class="hljs-keyword">AS</span> (
    <span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">ARRAY</span>&lt;<span class="hljs-keyword">STRUCT</span>&lt;warehouse <span class="hljs-keyword">string</span>, state <span class="hljs-keyword">string</span>&gt;&gt;
    [(<span class="hljs-string">'warehouse #1'</span>, <span class="hljs-string">'WA'</span>),
     (<span class="hljs-string">'warehouse #2'</span>, <span class="hljs-string">'CA'</span>),
     (<span class="hljs-string">'warehouse #3'</span>, <span class="hljs-string">'WA'</span>)] <span class="hljs-keyword">col</span>
)
<span class="hljs-keyword">SELECT</span> warehouse, state <span class="hljs-keyword">FROM</span> w, <span class="hljs-keyword">UNNEST</span>(w.col)
</code></pre>
<h4 id="heading-copiando-conteudo-de-uma-tabela-para-outra">Copiando conteúdo de uma tabela para outra</h4>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> dataset.DetailedInventory (product, quantity, supply_constrained)
<span class="hljs-keyword">SELECT</span> product, quantity, <span class="hljs-literal">false</span>
<span class="hljs-keyword">FROM</span> dataset.Inventory
</code></pre>
<h4 id="heading-insert-values-utilizando-uma-subconsulta"><code>INSERT VALUES</code> utilizando uma subconsulta</h4>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> dataset.DetailedInventory (product, quantity)
<span class="hljs-keyword">VALUES</span> (<span class="hljs-string">'countertop microwave'</span>,
        (<span class="hljs-keyword">SELECT</span> quantity <span class="hljs-keyword">FROM</span> dataset.DetailedInventory <span class="hljs-keyword">WHERE</span> product = <span class="hljs-string">'microwave'</span>))
</code></pre>
<h4 id="heading-insert-sem-utilizar-os-nomes-das-colunas"><code>INSERT</code> sem utilizar os nomes das colunas</h4>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> dataset.Warehouse <span class="hljs-keyword">VALUES</span>(<span class="hljs-string">'warehouse #4'</span>, <span class="hljs-string">'WA'</span>), (<span class="hljs-string">'warehouse #5'</span>, <span class="hljs-string">'NY'</span>)
</code></pre>
<h4 id="heading-utilizacao-do-insert-com-tipos-struct">Utilização do <code>INSERT</code> com tipos <code>STRUCT</code></h4>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> dataset.DetailedInventory
<span class="hljs-keyword">VALUES</span>
    (<span class="hljs-string">'top load washer'</span>, <span class="hljs-number">10</span>, <span class="hljs-literal">FALSE</span>, [(<span class="hljs-keyword">CURRENT_DATE</span>, <span class="hljs-string">"comment1"</span>)],(<span class="hljs-string">"white"</span>,<span class="hljs-string">"1 year"</span>,(<span class="hljs-number">30</span>,<span class="hljs-number">40</span>,<span class="hljs-number">28</span>))),
    (<span class="hljs-string">'front load washer'</span>, <span class="hljs-number">20</span>, <span class="hljs-literal">FALSE</span>, [(<span class="hljs-keyword">CURRENT_DATE</span>, <span class="hljs-string">"comment1"</span>)], (<span class="hljs-string">"beige"</span>,<span class="hljs-string">"1 year"</span>,(<span class="hljs-number">35</span>,<span class="hljs-number">45</span>,<span class="hljs-number">30</span>)))
</code></pre>
<p>Dado que a tabela tenha as colunas <code>comments</code> e <code>specifications</code> como um tipo <code>STRUCT</code>, com os valores seguindo a ordem dos campos, ela será incluída. Veja uma saída de uma consulta de exemplo:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>product</td><td>quantity</td><td>supply_constrained</td><td>comments</td><td>specifications</td><td></td></tr>
</thead>
<tbody>
<tr>
<td>front load washer</td><td>20</td><td>false</td><td><code>[{"created":"2021-02-09","comment":"comment1"}]</code></td><td><code>{"color":"beige","warranty":"1 year","dimensions":{"depth":"35.0","height":"45.0","width":"30.0"}}</code></td><td></td></tr>
<tr>
<td>top load washer</td><td>10</td><td>false</td><td><code>[{"created":"2021-02-09","comment":"comment1"}]</code></td><td><code>{"color":"white","warranty":"1 year","dimensions":{"depth":"30.0","height":"40.0","width":"28.0"}}</code></td></tr>
</tbody>
</table>
</div><h3 id="heading-insert-com-tipos-array"><code>INSERT</code> com tipos <code>ARRAY</code></h3>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">IF</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span> dataset.table1 (<span class="hljs-keyword">names</span> <span class="hljs-built_in">ARRAY</span>&lt;<span class="hljs-keyword">STRING</span>&gt;);

<span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> dataset.table1 (<span class="hljs-keyword">names</span>) <span class="hljs-keyword">VALUES</span> ([<span class="hljs-string">"name1"</span>,<span class="hljs-string">"name2"</span>])
</code></pre>
<h2 id="heading-clausula-update">Cláusula UPDATE</h2>
<p>A cláusula UPDATE é usada para atualizar os valores existentes em uma ou mais colunas em uma tabela. Ao criar consultas com ele, pode vir com outras cláusulas:</p>
<ul>
<li><p><code>WHERE</code>: as instruções <code>UPDATE</code> precisam incluir a cláusula <code>WHERE</code> com uma condição pelo menos. Caso precise atualizar todas as linhas de uma tabela, use <code>WHERE true</code>.</p>
</li>
<li><p><code>FROM</code>: o <code>UPDATE</code> pode utilizar opcionalmente a cláusula <code>FROM</code>, quando precisa especificar quais as linhas serão atualizadas na tabela de destino.</p>
</li>
</ul>
<p>Sobre o <code>FROM</code>, algumas advertências:</p>
<ol>
<li><p>A cláusula <code>SET</code> ela pode referir a colunas de uma tabela de destino e colunas de qualquer item <code>FROM</code>. Se tiver conflitos de nomes, as referências não qualificadas são tratadas como ambíguas.</p>
</li>
<li><p>Se a tabela de destino na cláusula <code>FROM</code> estiver presente, ela vai precisar ter um <em>alias</em> se quiser executar mesclagem automaticamente</p>
</li>
<li><p>Se uma linha de tabela estiver mesclada com zero linhas da cláusula <code>FROM</code>, ela não seria atualizada</p>
</li>
<li><p>Se uma linha de tabela estiver mesclada exatamente com uma linha da cláusula <code>FROM</code>, ela será atualizada</p>
</li>
<li><p>Se uma linha de tabela estiver mesclada com mais de uma linha da cláusula <code>FROM</code>, a consulta gerará o seguinte erro: <code>UPDATE/MERGE must match at most one source row for each target row</code>.</p>
</li>
</ol>
<p>Segue alguns exemplos de consultas SQL utilizando a cláusula <code>UPDATE</code>:</p>
<h4 id="heading-update-com-a-clausula-where"><code>UPDATE</code> com a cláusula <code>WHERE</code></h4>
<pre><code class="lang-sql"><span class="hljs-keyword">UPDATE</span> dataset.Inventory <span class="hljs-keyword">SET</span> quantity = quantity - <span class="hljs-number">10</span> <span class="hljs-keyword">WHERE</span> product <span class="hljs-keyword">like</span> <span class="hljs-string">'%washer%'</span>
</code></pre>
<h4 id="heading-update-usando-mesclagens"><code>UPDATE</code> usando mesclagens</h4>
<pre><code class="lang-sql"><span class="hljs-keyword">UPDATE</span> dataset.Inventory
<span class="hljs-keyword">SET</span> quantity = quantity + (<span class="hljs-keyword">SELECT</span> quantity <span class="hljs-keyword">FROM</span> dataset.NewArrivals 
                     <span class="hljs-keyword">WHERE</span> Inventory.product = NewArrivals.product),
    supply_constrained = <span class="hljs-literal">false</span>
<span class="hljs-keyword">WHERE</span> product <span class="hljs-keyword">IN</span> (<span class="hljs-keyword">SELECT</span> product <span class="hljs-keyword">FROM</span> dataset.NewArrivals)
</code></pre>
<p>Uma alternativa é associar as tabelas:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">UPDATE</span> dataset.Inventory i
<span class="hljs-keyword">SET</span> quantity = i.quantity + n.quantity, supply_constrained = <span class="hljs-literal">false</span>
<span class="hljs-keyword">FROM</span> dataset.NewArrivals n
<span class="hljs-keyword">WHERE</span> i.product = n.product
</code></pre>
<h4 id="heading-update-de-campos-aninhados"><code>UPDATE</code> de campos aninhados</h4>
<pre><code class="lang-sql"><span class="hljs-keyword">UPDATE</span> dataset.DetailedInventory
<span class="hljs-keyword">SET</span> specifications.color = <span class="hljs-string">'white'</span>,specifications.warranty = <span class="hljs-string">'1 year'</span>
<span class="hljs-keyword">WHERE</span> product <span class="hljs-keyword">like</span> <span class="hljs-string">'%washer%'</span>
</code></pre>
<p>Como alternativa, atualize todo o registro:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">UPDATE</span> dataset.DetailedInventory
<span class="hljs-keyword">SET</span> specifications = <span class="hljs-keyword">STRUCT</span>&lt;color <span class="hljs-keyword">STRING</span>, warranty <span class="hljs-keyword">STRING</span>, dimensions <span class="hljs-keyword">STRUCT</span>&lt;<span class="hljs-keyword">depth</span> FLOAT64, height FLOAT64, width FLOAT64&gt;&gt;(<span class="hljs-string">'white'</span>, <span class="hljs-string">'1 year'</span>, <span class="hljs-literal">NULL</span>)
<span class="hljs-keyword">WHERE</span> product <span class="hljs-keyword">like</span> <span class="hljs-string">'%washer%'</span>
</code></pre>
<h4 id="heading-update-de-registros-repetidos"><code>UPDATE</code> de registros repetidos</h4>
<pre><code class="lang-sql"><span class="hljs-keyword">UPDATE</span> dataset.DetailedInventory
<span class="hljs-keyword">SET</span> comments = <span class="hljs-built_in">ARRAY</span>(
    <span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">comment</span> <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">UNNEST</span>(comments) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">comment</span>  
    <span class="hljs-keyword">UNION</span> <span class="hljs-keyword">ALL</span>  
    <span class="hljs-keyword">SELECT</span> (<span class="hljs-keyword">CAST</span>(<span class="hljs-string">'2016-01-01'</span> <span class="hljs-keyword">AS</span> <span class="hljs-built_in">DATE</span>), <span class="hljs-string">'comment1'</span>)
)
<span class="hljs-keyword">WHERE</span> product <span class="hljs-keyword">like</span> <span class="hljs-string">'%washer%'</span>
</code></pre>
<blockquote>
<p><strong>Dica</strong><br />Se a coluna for do tipo <code>STRUCT</code>, o <code>column_name</code> pode referenciar um campo usando a notação de ponto, como por exemplo: <code>struct1.field1</code>.</p>
</blockquote>
<h2 id="heading-clausula-delete">Cláusula DELETE</h2>
<p>A cláusula DELETE é usada para remover linhas específicas de uma tabela. É importante explicar que a cláusula <code>WHERE</code> é obrigatória para esse tipo de instrução. Caso precise excluir todas as linhas de uma tabela, defina o <code>WHERE</code> como <code>true</code>, como no exemplo abaixo:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> dataset.DetailedInventory <span class="hljs-keyword">WHERE</span> <span class="hljs-literal">true</span>
</code></pre>
<p>Segue mais alguns exemplos:</p>
<h4 id="heading-delete-com-clausula-where"><code>DELETE</code> com cláusula <code>WHERE</code></h4>
<pre><code class="lang-sql"><span class="hljs-keyword">DELETE</span> dataset.Inventory <span class="hljs-keyword">WHERE</span> quantity = <span class="hljs-number">0</span>
</code></pre>
<h4 id="heading-delete-com-uma-subconsulta"><code>DELETE</code> com uma subconsulta</h4>
<pre><code class="lang-sql"><span class="hljs-keyword">DELETE</span> dataset.Inventory i
<span class="hljs-keyword">WHERE</span> i.product <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">IN</span> (<span class="hljs-keyword">SELECT</span> product <span class="hljs-keyword">from</span> dataset.NewArrivals)
</code></pre>
<h4 id="heading-delete-com-subconsulta-utilizando-a-clausula-exists"><code>DELETE</code> com subconsulta, utilizando a cláusula <code>EXISTS</code></h4>
<pre><code class="lang-sql"><span class="hljs-keyword">DELETE</span> dataset.Inventory
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span>  
    (<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">from</span> dataset.NewArrivals <span class="hljs-keyword">WHERE</span> Inventory.product = NewArrivals.product)
</code></pre>
<h2 id="heading-clausula-truncate-table">Cláusula TRUNCATE TABLE</h2>
<p>A cláusula TRUNCATE TABLE é uma operação que permite remover todos os dados de uma tabela de uma só vez. Ao contrário da cláusula <code>DELETE</code>, que remove linhas específicas de uma tabela, a cláusula TRUNCATE TABLE <strong>remove todos os dados da tabela, sem levar em consideração nenhuma condição específica</strong>. Portanto, é importante ter cuidado ao utilizar essa cláusula para evitar a perda acidental de dados importantes.</p>
<p>A seguir, um exemplo de como usar a cláusula TRUNCATE TABLE no BigQuery:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">TRUNCATE</span> <span class="hljs-keyword">TABLE</span> dataset.Inventory
</code></pre>
<h2 id="heading-clausula-merge">Cláusula MERGE</h2>
<p>A cláusula MERGE é uma operação suportada pelo BigQuery que combina as operações de INSERT, UPDATE e DELETE em uma única consulta SQL. Essa operação é usada para combinar dados de várias tabelas em uma única tabela, ou para atualizar ou inserir novas linhas em uma tabela existente com base em uma condição de correspondência.</p>
<p>Ela é útil quando se trabalha com grandes volumes de dados em diferentes fontes, pois permite combinar dados de forma mais eficiente e precisa. O BigQuery permite que você execute consultas MERGE usando instruções SQL padrão, o que torna a integração de dados mais fácil e eficiente.</p>
<p>Suponha que você tenha duas tabelas, "customers_old" e "customers_new", que contêm informações sobre clientes. A tabela "customers_old" contém informações desatualizadas, enquanto a tabela "customers_new" contém informações atualizadas. Você pode usar a cláusula <code>MERGE</code> para combinar as duas tabelas e atualizar as informações desatualizadas na tabela "customers_old".</p>
<pre><code class="lang-sql"><span class="hljs-keyword">MERGE</span> customers_old <span class="hljs-keyword">AS</span> target
<span class="hljs-keyword">USING</span> customers_new <span class="hljs-keyword">AS</span> <span class="hljs-keyword">source</span>
<span class="hljs-keyword">ON</span> target.customer_id = source.customer_id
<span class="hljs-keyword">WHEN</span> <span class="hljs-keyword">MATCHED</span> <span class="hljs-keyword">THEN</span>
  <span class="hljs-keyword">UPDATE</span> <span class="hljs-keyword">SET</span> target.name = source.name, target.email = source.email
<span class="hljs-keyword">WHEN</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">MATCHED</span> <span class="hljs-keyword">THEN</span>
  <span class="hljs-keyword">INSERT</span> (customer_id, <span class="hljs-keyword">name</span>, email)
  <span class="hljs-keyword">VALUES</span> (source.customer_id, source.name, source.email);
</code></pre>
<p>Nessa consulta, a cláusula MERGE combina as tabelas "customers_old" e "customers_new" com base no campo "customer_id". A cláusula WHEN MATCHED é usada para atualizar as informações desatualizadas na tabela "customers_old" com as informações atualizadas da tabela "customers_new". A cláusula WHEN NOT MATCHED é usada para inserir novas linhas na tabela "customers_old" com informações da tabela "customers_new" que não existiam anteriormente.</p>
<p>Vamos a um outro exemplo. Suponha que você tenha uma tabela chamada "orders", que contém informações sobre pedidos de clientes, incluindo o ID do cliente, a data do pedido e o valor total. Você pode usar a cláusula MERGE para inserir novos pedidos na tabela "orders" ou atualizar os valores dos pedidos existentes com base no ID do cliente e na data do pedido.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">MERGE</span> orders <span class="hljs-keyword">AS</span> target
<span class="hljs-keyword">USING</span> (
  <span class="hljs-keyword">SELECT</span> customer_id, order_date, <span class="hljs-keyword">SUM</span>(price) <span class="hljs-keyword">as</span> total_price
  <span class="hljs-keyword">FROM</span> new_orders
  <span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> customer_id, order_date
) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">source</span>
<span class="hljs-keyword">ON</span> target.customer_id = source.customer_id <span class="hljs-keyword">AND</span> target.order_date = source.order_date
<span class="hljs-keyword">WHEN</span> <span class="hljs-keyword">MATCHED</span> <span class="hljs-keyword">THEN</span>
  <span class="hljs-keyword">UPDATE</span> <span class="hljs-keyword">SET</span> target.total_price = source.total_price
<span class="hljs-keyword">WHEN</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">MATCHED</span> <span class="hljs-keyword">THEN</span>
  <span class="hljs-keyword">INSERT</span> (customer_id, order_date, total_price)
  <span class="hljs-keyword">VALUES</span> (source.customer_id, source.order_date, source.total_price);
</code></pre>
<p>Nessa consulta, a cláusula MERGE combina a tabela "orders" com uma subconsulta "new_orders", que contém informações sobre novos pedidos de clientes. A cláusula ON é usada para comparar o ID do cliente e a data do pedido em ambas as tabelas. A cláusula WHEN MATCHED é usada para atualizar os valores de pedidos existentes na tabela "orders" com os valores da subconsulta "new_orders". A cláusula WHEN NOT MATCHED é usada para inserir novos pedidos na tabela "orders" que não existiam anteriormente.</p>
<p>Para quem já tem familiaridade com bancos relacionais e SQL, vai se identificar com a maioria dos comandos aqui, e isso é bom, porque a curva de aprendizado é bem menor.</p>
]]></content:encoded></item><item><title><![CDATA[Tipos de Dados no BigQuery: Como Armazenar e Consultar Diferentes Tipos de Dados em SQL]]></title><description><![CDATA[O BigQuery utiliza diferentes tipos de dados que permitem a organização e classificação dos dados para consultas mais eficientes. Segue os tipos de dados suportados:
Tipos de Dados Numéricos
Os tipos de dados numéricos são usados para armazenar valor...]]></description><link>https://blog.gilsondev.in/tipos-de-dados-no-bigquery-como-armazenar-e-consultar-diferentes-tipos-de-dados-em-sql</link><guid isPermaLink="true">https://blog.gilsondev.in/tipos-de-dados-no-bigquery-como-armazenar-e-consultar-diferentes-tipos-de-dados-em-sql</guid><category><![CDATA[Databases]]></category><category><![CDATA[NoSQL]]></category><category><![CDATA[bigquery]]></category><category><![CDATA[google cloud]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Sat, 25 Feb 2023 00:23:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/oyXis2kALVg/upload/afef38c5b060a202f1dc8945e08b8ddc.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>O BigQuery utiliza diferentes tipos de dados que permitem a organização e classificação dos dados para consultas mais eficientes. Segue os tipos de dados suportados:</p>
<h2 id="heading-tipos-de-dados-numericos">Tipos de Dados Numéricos</h2>
<p>Os tipos de dados numéricos são usados para armazenar valores numéricos. Os principais tipos de dados numéricos são:</p>
<ul>
<li><p><code>INT64</code>: um número inteiro de 64 bits, que pode ser positivo ou negativo.</p>
</li>
<li><p><code>FLOAT64</code>: um número de ponto flutuante de 64 bits, que pode representar valores com decimais.</p>
</li>
<li><p><code>NUMERIC</code>: um número decimal preciso, que pode ser usado para cálculos que requerem precisão.</p>
</li>
</ul>
<p>Exemplo de consulta SQL utilizando o tipo de dados numéricos:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">SUM</span>(price) <span class="hljs-keyword">as</span> total_price
<span class="hljs-keyword">FROM</span> orders
<span class="hljs-keyword">WHERE</span> customer_id = <span class="hljs-number">12345</span>;
</code></pre>
<h2 id="heading-tipos-de-dados-de-data-e-hora">Tipos de Dados de Data e Hora</h2>
<p>Os tipos de dados de data e hora são usados para armazenar datas e horários. Os principais tipos de dados de data e hora são:</p>
<ul>
<li><p><code>DATE</code>: uma data no formato YYYY-MM-DD.</p>
</li>
<li><p><code>TIMESTAMP</code>: um horário UTC com precisão de segundos.</p>
</li>
<li><p><code>DATETIME</code>: um horário com precisão de microssegundos.</p>
</li>
</ul>
<p>Exemplo de consulta SQL utilizando o tipo de dados de data e hora:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">COUNT</span>(*) <span class="hljs-keyword">as</span> num_orders
<span class="hljs-keyword">FROM</span> orders
<span class="hljs-keyword">WHERE</span> <span class="hljs-built_in">DATE</span>(order_date) = <span class="hljs-string">'2022-01-01'</span>;
</code></pre>
<h2 id="heading-tipos-de-dados-textuais">Tipos de Dados Textuais</h2>
<p>Os tipos de dados textuais são usados para armazenar texto. Os principais tipos de dados textuais são:</p>
<ul>
<li><p><code>STRING</code>: um texto com tamanho máximo de 2 GB.</p>
</li>
<li><p><code>BYTES</code>: um valor de byte com tamanho máximo de 2 GB.</p>
</li>
</ul>
<p>Exemplo de consulta SQL utilizando o tipo de dados textual:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">COUNT</span>(*) <span class="hljs-keyword">as</span> num_customers
<span class="hljs-keyword">FROM</span> customers
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">name</span> <span class="hljs-keyword">LIKE</span> <span class="hljs-string">'%John%'</span>;
</code></pre>
<h2 id="heading-tipos-de-dados-geograficos">Tipos de Dados Geográficos</h2>
<p>Os tipos de dados geográficos são usados para armazenar informações de localização. Os principais tipos de dados geográficos são:</p>
<ul>
<li><p><code>GEOGRAPHY</code>: um ponto, linha ou polígono no formato WKT ou GeoJSON.</p>
</li>
<li><p><code>ST_GEOGPOINT</code>: um ponto geográfico com coordenadas de latitude e longitude.</p>
</li>
<li><p><code>ST_GEOGPOLYGON</code>: um polígono geográfico definido por uma lista de pontos.</p>
</li>
</ul>
<p>Exemplo de consulta SQL utilizando o tipo de dados geográficos:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">COUNT</span>(*) <span class="hljs-keyword">as</span> num_locations
<span class="hljs-keyword">FROM</span> locations
<span class="hljs-keyword">WHERE</span> ST_CONTAINS(region, ST_GEOGPOINT(<span class="hljs-number">-122.4194</span>, <span class="hljs-number">37.7749</span>));
</code></pre>
<h2 id="heading-tipos-de-dados-estruturados-struct">Tipos de Dados Estruturados: STRUCT</h2>
<p>Os tipos de dados estruturados são usados para armazenar dados em uma estrutura hierárquica. Os principais tipos de dados estruturados são:</p>
<ul>
<li><code>STRUCT</code>: uma estrutura aninhada que pode conter campos com diferentes tipos de dados.</li>
</ul>
<p>Suponha que você tem uma tabela no BigQuery que contém informações de clientes, incluindo nome, endereço e informações de contato. A tabela pode ser estruturada como uma lista de structs, com cada struct contendo informações sobre um único cliente:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> customers (
  <span class="hljs-keyword">id</span> INT64,
  <span class="hljs-keyword">name</span> <span class="hljs-keyword">STRING</span>,
  address <span class="hljs-keyword">STRUCT</span>&lt;street <span class="hljs-keyword">STRING</span>, city <span class="hljs-keyword">STRING</span>, state <span class="hljs-keyword">STRING</span>, zip INT64&gt;,
  contact <span class="hljs-keyword">STRUCT</span>&lt;phone <span class="hljs-keyword">STRING</span>, email <span class="hljs-keyword">STRING</span>&gt;
);
</code></pre>
<p>Agora, para recuperar o endereço completo de um cliente específico, você pode usar o tipo de dados STRUCT em uma consulta SQL:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> address.street, address.city, address.state, address.zip
<span class="hljs-keyword">FROM</span> customers
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">id</span> = <span class="hljs-number">12345</span>;
</code></pre>
<p>Nessa consulta, a estrutura "address" é um struct que contém informações sobre o endereço do cliente. A consulta seleciona o endereço completo do cliente com ID 12345, mostrando a rua, a cidade, o estado e o código postal.</p>
<p>Note que, para selecionar campos dentro de uma estrutura, é necessário especificar o nome da estrutura seguido do nome do campo, separados por um ponto. Isso permite selecionar campos específicos de um struct e criar consultas mais precisas e eficientes.</p>
<h2 id="heading-tipo-array">Tipo ARRAY</h2>
<p>O tipo de dado <code>ARRAY</code> é usado para armazenar uma lista de valores do mesmo tipo em uma única coluna em uma tabela do BigQuery. Esses valores podem ser de qualquer tipo de dado suportado pela ferramenta , como números, strings, data/hora, geográficos, etc.</p>
<p>Ao armazenar dados em um array, é possível organizar as informações em uma única coluna e simplificar a estrutura da tabela, tornando mais fácil a consulta e análise dos dados. Com o tipo de dado ARRAY, é possível, por exemplo, armazenar todos os produtos comprados por um cliente em uma única linha da tabela.</p>
<p>Suponha que você tenha uma tabela no BigQuery que contém informações de pedidos de clientes, incluindo o ID do cliente e uma lista de produtos comprados. A tabela pode ser estruturada da seguinte maneira:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> orders (
  <span class="hljs-keyword">id</span> INT64,
  customer_id INT64,
  products <span class="hljs-built_in">ARRAY</span>&lt;<span class="hljs-keyword">STRUCT</span>&lt;product_name <span class="hljs-keyword">STRING</span>, quantity INT64&gt;&gt;
);
</code></pre>
<p>Nessa estrutura, a coluna "products" é um array que contém informações sobre os produtos comprados pelo cliente em cada pedido. Para recuperar o número total de produtos comprados por um cliente específico, você pode usar o tipo de dados ARRAY em uma consulta SQL:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">SUM</span>(product.quantity) <span class="hljs-keyword">as</span> total_quantity
<span class="hljs-keyword">FROM</span> orders, <span class="hljs-keyword">UNNEST</span>(products) <span class="hljs-keyword">as</span> product
<span class="hljs-keyword">WHERE</span> customer_id = <span class="hljs-number">12345</span>;
</code></pre>
<p>Nessa consulta, a função <code>UNNEST</code> é usada para desaninhar a estrutura de produtos, permitindo que cada linha da tabela corresponda a um único produto comprado pelo cliente. O resultado da consulta é o número total de produtos comprados pelo cliente com ID 12345.</p>
<p>O BigQuery é uma ferramenta poderosa para armazenar e consultar dados em nuvem, permitindo a análise de grandes volumes de dados em tempo real. Com uma variedade de tipos de dados disponíveis, é possível armazenar e classificar diferentes tipos de dados em uma estrutura hierárquica, o que pode facilitar o processo de consulta e análise de dados. Ao conhecer os tipos de dados disponíveis e como utilizá-los em suas consultas SQL, você poderá extrair informações valiosas de seus dados em menos tempo e com maior eficiência.</p>
]]></content:encoded></item><item><title><![CDATA[BigQuery: o serviço de armazenamento de dados do Google Cloud Platform]]></title><description><![CDATA[Se você está em busca de uma ferramenta de armazenamento e análise de dados em nuvem, certamente já deve ter ouvido falar do BigQuery, o serviço de armazenamento de dados oferecido pelo Google Cloud Platform. Se ainda não conhece ou está em busca de ...]]></description><link>https://blog.gilsondev.in/bigquery-o-servico-de-armazenamento-de-dados-do-google-cloud-platform</link><guid isPermaLink="true">https://blog.gilsondev.in/bigquery-o-servico-de-armazenamento-de-dados-do-google-cloud-platform</guid><category><![CDATA[bigquery]]></category><category><![CDATA[data-engineering]]></category><category><![CDATA[google cloud]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Sat, 25 Feb 2023 00:17:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/WEizaiwLk1k/upload/0c44f38f9f53f4bad1897b577ecfe713.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Se você está em busca de uma ferramenta de armazenamento e análise de dados em nuvem, certamente já deve ter ouvido falar do BigQuery, o serviço de armazenamento de dados oferecido pelo Google Cloud Platform. Se ainda não conhece ou está em busca de mais informações, continue lendo para descobrir o que você precisa saber sobre o essa ferramenta.</p>
<h2 id="heading-o-que-e-o-bigquery">O que é o BigQuery?</h2>
<p>Ele é um Modern Data Warehouse da Google, em que possui uma estrutura de armazenamento com a capacidade e infraestrutura do Cloud da Google, em que as consultas são feitas via SQL. Você pode fazer as chamadas na plataforma, mas também pode ser feito através da linha de comando, em chamadas REST ou usando as bibliotecas como .NET, Java ou Python. Ele foi feito para lidar com grande volume de dados, na casa dos Petabytes. Ele não tem servidor, portanto não tem necessidade de uma infraestrutura para gerenciar o ambiente.</p>
<p>Além disso ele é rápido por conta da forma com que os dados são armazenados, utilizando a forma colunar. Essa estrutura visa diminuir o processo de I/O de disco ao mínimo, lendo a menor quantidade de dados durante a consulta. Além disso, diferente do relacional, em que é usado em aplicativos transacionais, no colunar ele é otimizado para uma recuperação rápida de dados, normalmente usados em aplicações analíticas.</p>
<p>Por conta da otimização na leitura dos dados em disco, e na forma em que a estrutura foi pensada, ela é ideal para soluções como Data Warehouse e Processamento de Big Data.</p>
<p>Por essas características, ela é uma excelente opção para empresas que precisam armazenar e analisar grandes volumes de dados em tempo real. A plataforma oferece um ambiente seguro e escalável para armazenamento de dados, além de permitir o acesso a dados em tempo real e a execução de consultas complexas em segundos. O BigQuery também é altamente integrado com outras soluções do Google Cloud Platform, como o Google Analytics e o Google Data Studio.</p>
<h2 id="heading-quais-sao-suas-vantagens">Quais são suas vantagens?</h2>
<ul>
<li><p><strong>Análise em tempo real</strong>: Com a performance dela podemos fazer inclusões de streaming, além de permitir capturar e analisar os dados praticamente online para gerar insights rápidos. Os dados novos ficam disponíveis imediatamente.</p>
</li>
<li><p><strong>Acessível a grandes e pequenas companhias</strong>: Por conta da forma de cobrança, ela é flexível no orçamento para qualquer tamanho de empresa. Para pequenas empresas por exemplo, temos as cotas mensais gratuitas (até 1Tb/mês de dados analisados e 10Gb de dados armazenados), e para as grandes é costume optar pela escala, com contrato focado na disponibilidade e suporte.</p>
</li>
<li><p><strong>Dispensa servidor</strong>: Como ela se beneficia da infraestrutura da Google, ele pode focar somente nos insights gerados através das consultas.</p>
</li>
<li><p><strong>SQL padrão</strong>: Ele utiliza o padrão ANSI:2011, o que reduz a necessidade de reescritas e novos aprendizados. Além disso disponibiliza drivers (ex.: ODBC e JDBC) sem custo.</p>
</li>
<li><p><strong>Separação de armazenamento e computação</strong>: Ele possui uma estrutura escalonada, facilitando a capacidade de armazenar os dados, e que por consequência resulta em custos menores. Além disso, o armazenamento pode ser cobrado separadamente das consultas. De acordo com o Google, o BigQuery viabiliza a redução de custos em 56% a 88%.</p>
</li>
<li><p><strong>Modelo de preços flexíveis</strong>: Você escolhe a opção de pagar somente armazenamento e recursos utilizados. Em outros modelos, como fixo, pode escolher um custo mensavel escalável, de acordo com seu volume de dados.</p>
</li>
<li><p><strong>Compartilhamento otimizado</strong>: Você pode compartilhar o acesso a sua base para usuários internos ou externos, tendo segurança de autenticação e papéis, além de saber o custo gerados nas consultas a partir de cada usuário.</p>
</li>
<li><p><strong>Descontos automáticos</strong>: Se você tem utilizado e armazenado os dados com grande frequência, nos primeiros 90 dias o Google automaticamente aplica descontos.</p>
</li>
<li><p><strong>Importação de dados</strong>: é possível fazer a importação dos dados a partir de arquivos como CSV, JSON, Avro ou Parquet.</p>
</li>
<li><p><strong>Machine Learning</strong>: Com o BigQuery ML você pode criar, testar e utilizar modelos de Machine Learning nos dados em escala global, usando SQL simples.</p>
</li>
</ul>
<h2 id="heading-criando-sua-conta-no-gcp">Criando sua conta no GCP</h2>
<p>Para criar uma assinatura no Google Cloud, você pode utilizar alguma conta Google que utiliza os recursos do Workspace por exemplo (Gmail, Google Docs, etc). Nos primeiros 90 dias você ganha um crédito de $300 para gastar nos serviços. Clique em "Comece agora gratuitamente"</p>
<p><img src="https://i.imgur.com/lxvh6xa.png" alt /></p>
<p>Feito isso ele vai fazer algumas perguntas em 3 etapas.</p>
<p><strong>Etapa 1</strong></p>
<p><img src="https://i.imgur.com/4tAK8AG.png" alt /></p>
<p><strong>Etapa 2</strong><br />Nesse caso esse usuário já preencheu algumas informações, que caso seja acessado pela primeira vez, pode ser solicitado como:</p>
<ul>
<li><p>Tipo de Conta</p>
</li>
<li><p>CPF ou CNPJ</p>
</li>
<li><p>Endereço</p>
</li>
<li><p>Data de Nascimento (em PF)</p>
</li>
<li><p>Dados de pagamento</p>
</li>
</ul>
<p>Sobre os dados de pagamento o Google solicita, mas não quer dizer que será cobrado, isso é feito para fins de evitar criação de contas por robôs. Você só será cobrado se alterar isso manualmente na sua conta. Lembrando que se passar dos 90 dias, você precisa atualizar para uma conta paga.</p>
<p><strong>Etapa 3</strong><br />Com tudo isso feito, você será redirecionado para um dashboard principal do Google Cloud.</p>
<p><img src="https://i.imgur.com/8QFto8n.png" alt /></p>
<p>Na primeira vez é provavel que o conteúdo dele esteja em branco, mas no caso da imagem acima eu criei um novo projeto para trabalhar em cima. Ele traz algumas informações relacionados a ela.</p>
<h3 id="heading-criando-um-novo-projeto">Criando um novo projeto</h3>
<p>Para o GCP um projeto é um escopo que você aloca um conjunto de recursos a ela, então pode no Cloud vários projetos. Para isso você vai na barra superior (ao lado do "Google Cloud Platform") e selecione o campo. Vai aparecer uma modal com a lista de projetos cadastrado, e opção de incluir um novo.</p>
<p><img src="https://i.imgur.com/SYTJqVT.png" alt /></p>
<p>Ao solicitar a criação de um novo projeto você vai preencher os seguintes dados:</p>
<ul>
<li><p>Nome do projeto</p>
</li>
<li><p>ID do projeto (opcional porque ele é gerado automaticamente)</p>
</li>
<li><p>Qual organização deseja que esse projeto esteja vinculado</p>
</li>
<li><p>Local (pode ser uma pasta ou organização pai)</p>
</li>
</ul>
<p><img src="https://i.imgur.com/KSYgu6n.png" alt /></p>
<p><img src="https://i.imgur.com/oPFMtVA.png" alt /></p>
<p>Depois de criado você pode selecionar e vincular os recursos do GCP que deseja nele.</p>
<h2 id="heading-criando-sua-primeira-consulta">Criando sua primeira consulta</h2>
<p>Vamos fazer a nossa primeira consulta, e para precisa ter uma conta criada e logada, além de ter criado um projeto. Para acessar a ferramenta, vamos no menu lateral e ir na seção <strong>Big Data</strong>.</p>
<p><img src="https://i.imgur.com/tBKy2N3.png" alt /></p>
<p>Clicando na opção, vamos ser redirecionados ao <em>Espaço de trabalho SQL</em> em que teremos acesso ao editor de texto, em que iremos executar as nossas consultas.</p>
<p><img src="https://i.imgur.com/BDduYLv.png" alt /></p>
<p>Um detalhe importante é que no topo do editor, é exibido um alerta sobre o <em>Sandbox</em>. Ele é preparado para você experimentar a ferramenta e fazer as consultas e armazenamentos, mas com determinados limites. <a target="_blank" href="https://cloud.google.com/bigquery/docs/sandbox?hl=pt-br">Acesse a documentação</a> para entender mais. Agora vamos fazer a primeira consulta, usando uma <a target="_blank" href="https://cloud.google.com/bigquery/public-data">base de dados pública</a> que o BigQuery disponibiliza.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> gender, tripduration <span class="hljs-keyword">FROM</span> <span class="hljs-string">`bigquery-public-data.new_york_citibike.citibike_trips`</span> <span class="hljs-keyword">LIMIT</span> <span class="hljs-number">5</span>
</code></pre>
<p>Aqui estamos trazendo duas colunas, de um dataset público, que são os dados sobre o uso de bicicletas na cidade de Nova York. A consulta é simples, mas existe uma peculiaridade. Podemos ler a query da seguinte forma:</p>
<blockquote>
<p>Busque as colunas <code>gender</code> e <code>tripduration</code> que está na tabela <code>citibike_trips</code>, do dataset <code>new_york_citibike</code>, do projeto <code>bigquery-public-data</code>.</p>
<p><strong>Atenção!</strong></p>
<p>Como estamos acessando algo fora do nosso dataset e projeto, essa sintaxe é necessária. Mas no caso da nossa própria base, podemos definir o nome da tabela somente. Além disso, é importante explicar que:</p>
<ul>
<li><p>No nosso projeto podemos ter vários datasets</p>
</li>
<li><p>No nosso dataset podemos ter várias tabelas</p>
</li>
</ul>
</blockquote>
<p>Nesse exemplo vemos o quão semelhante uma query no BigQuery é semelhante ao que conhecemos do SQL em bancos relacionais.</p>
]]></content:encoded></item><item><title><![CDATA[Banco de dados e SQL - Introdução e História]]></title><description><![CDATA[Depois de algum tempo trabalhando na web, é comum você se deparar com bibliotecas que faz um intermédio da sua aplicação com o banco, de uma forma mais orientado a objetos, o conhecido ORM (Object Relational Mapping). Ele nos oferece muitas coisas bo...]]></description><link>https://blog.gilsondev.in/banco-de-dados-e-sql-introducao-e-historia</link><guid isPermaLink="true">https://blog.gilsondev.in/banco-de-dados-e-sql-introducao-e-historia</guid><category><![CDATA[SQL]]></category><category><![CDATA[Databases]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Tue, 01 Nov 2022 17:49:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/bqzLehtF8XE/upload/v1667324914343/WZZgUTrweL.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Depois de algum tempo trabalhando na web, é comum você se deparar com bibliotecas que faz um intermédio da sua aplicação com o banco, de uma forma mais orientado a objetos, o conhecido ORM (Object Relational Mapping). Ele nos oferece muitas coisas boas como:</p>
<ul>
<li>Melhor produtividade</li>
<li>Coerência no código, sem usar e abusar de queries SQL, usando o paradigma de programação da aplicação.</li>
<li>Facilita na mudança de banco de dados (às vezes, não vem ao caso)</li>
</ul>
<p>Mesmo com seus prós e contras ele ajuda muito, só tem um problema.</p>
<blockquote>
<p>Com toda essa dependência, você fica “mais ignorante” com o SQL.</p>
</blockquote>
<p>Claro que alguns podem discordar, e fiquem a vontade para falarmos nos comentários, mas conversando com alguns colegas, creio que o sentimento é parecido: você perde a “memória muscular” do SQL ou peculiaridades do banco e suas boas práticas. Por isso, achei legal dar uma revisada sobre banco de dados e principalmente sobre essa linguagem de consulta, para justamente não confiar cegamente nessas bibliotecas, porque elas não são perfeitas, e um dia vai ser necessário customizar o comportamento delas para se tornar mais performática.</p>
<p>Irei criar posts voltado a esses estudos, e hoje vamos falar um pouco da história do banco de dados e como surgiu o SQL como conhecemos hoje.</p>
<h2 id="heading-comecando-do-inicio">Começando do início</h2>
<p>Antes de mais nada, o que é um banco de dados? Basicamente é um conjunto de informações relacionadas. Na <a target="_blank" href="https://pt.wikipedia.org/wiki/Banco_de_dados">wikipédia</a> diz:</p>
<blockquote>
<p>Bancos de dados ou bases de dados são um conjunto de arquivos relacionados entre si com registros sobre pessoas, lugares ou coisas. São coleções organizadas de dados que se relacionam de forma a criar algum sentido (Informação) e dar mais eficiência durante uma pesquisa ou estudo.</p>
</blockquote>
<p>O caso de uma lista telefônica pode ser considerado um banco de dados. Quando tinhamos um caderninho com os nomes das pessoas e seus telefones eles eram organizados de forma estruturada e os dados eram <strong>relacionados e que tinham a informação</strong> para entrarmos em contato, quando necessário. Em banco de dados físico, tinham suas desvantagens:</p>
<ul>
<li>Pesquisar os dados necessários poderia levar a algum tempo, em caso de muitos registros</li>
<li>A mudança de dados, de forma frequente, tornava mais desgastante e custava mais, quando era impresso.</li>
</ul>
<p>Depois veio os bancos de dados que eram armazenados em fitas magnéticas, e veio tornar mais fácil, mas claro ainda estava aquém do ideal, como limitações de hardware na época.</p>
<p>Ao longo dos anos, surgiram outras soluções de banco de dados, algumas com modelos de dados específicos.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667324748514/JC1Ezg-_5.png" alt="image.png" class="image--center mx-auto" /></p>
<h2 id="heading-tipos-de-modelo-de-dados">Tipos de Modelo de dados</h2>
<p>Em Sistemas de Banco de Dados, temos uma variedade de tipos de base de dados, ou seja, em como esses dados estão organizados:</p>
<h3 id="heading-hierarquico">Hierárquico</h3>
<p>Trabalha com conceito de árvore, usando ligações entre os registros.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667324796507/eqKn1sj5k.png" alt="image.png" class="image--center mx-auto" /></p>
<h3 id="heading-redes">Redes</h3>
<p>Possuem registros e ligações que definem os relacionamentos entre os registros.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667324815263/3wqHscTnA.png" alt="image.png" class="image--center mx-auto" /></p>
<h3 id="heading-relacional">Relacional</h3>
<p>Representação dos dados em conjunto de tabelas. Esse modelo foi idealizado por Edgar Frank Codd em 1970, sendo descrito no artigo <em>“Relational Model of Data for Large Shared Data Banks”</em>. Esse tipo que usamos até hoje e iremos focar.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667324835156/2W0m5ZWE9.png" alt="image.png" class="image--center mx-auto" /></p>
<h2 id="heading-sql-e-sua-historia">SQL e sua história</h2>
<p>Além do modelo relacional, o Codd propôs uma linguagem para manipular os dados em tabelas, e que foi chamado de DSL/Alpha. Com a publicação de um artigo com essa proposta, a IBM auxiliou para que fosse possível criar o protótipo, e assim foi criado uma versão inicial, chamado de SQUARE. Após o seu lançamento, ele sofreu melhorias e que levou a mudança do nome para SEQUEL que foi finalmente renomeada para SQL. Um dos principais desenvolvedores foram Donald D. Chamberlin e Raymond F. Boyce.</p>
<p>No SQL temos partes distintas na linguagem:</p>
<ul>
<li>Instruções de esquema SQL: Usados para definir os objetos do banco de dados em que os dados serão armazenados (tabela, chave primária, etc);</li>
<li>Instruções de dados SQL: Usados para manipular as estrutura de dados definidas previamente;</li>
<li>Instruções de transação SQL: Usado para iniciar, encerrar e desfazer transações</li>
</ul>
<p>Alguns exemplos:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> contatos(  
<span class="hljs-keyword">id</span> <span class="hljs-built_in">SMALLINT</span>,  
nome <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,  
telefone <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">30</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,  
<span class="hljs-keyword">CONSTRAINT</span> pk_contatos PRIMARY <span class="hljs-keyword">KEY</span> (<span class="hljs-keyword">id</span>)  
);
</code></pre>
<p>Foi usado acima a <strong>instrução de esquema</strong> para criar a tabela <em>contatos</em> no banco de dados. No caso de inserir e consultar um novo registro, respectivamente, usamos a <strong>instrução de dados</strong>:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> contatos (<span class="hljs-keyword">id</span>, nome, telefone) <span class="hljs-keyword">VALUES</span> (<span class="hljs-number">1</span>, <span class="hljs-string">'Carlos Silva'</span>, <span class="hljs-string">'(61) 99999-1111'</span>);<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> contatos;
</code></pre>
<p>Essa linguagem é intrinsecamente ligado a modelo relacional porque o resultado de uma consulta gera uma tabela, ou o conhecido <em>result set</em>. Com isso podemos usar tanto as tabelas permanentes, criados via instrução de esquema, como as tabelas do tipo <em>result set</em>, como entradas para futuras consultas.</p>
<h2 id="heading-conclusao">Conclusão</h2>
<p>Dessa forma, espero que tenha sido interessante essa breve introdução e história do banco de dados e como surgiu o SQL.</p>
<p>Até mais.</p>
]]></content:encoded></item><item><title><![CDATA[Sou um procrastinador, e hoje estou bem com isso]]></title><description><![CDATA[Antes de começar: Esse post eu criei em 2017 no Medium, mas ele continua atual, ainda mais em relação com o meu recente diagnóstico de que tenho TDAH e sinais leves de TEA (Transtorno do Expectro Autista). Eu ainda estou assimilando tudo isso, mas no...]]></description><link>https://blog.gilsondev.in/sou-um-procrastinador-e-hoje-estou-bem-com-isso</link><guid isPermaLink="true">https://blog.gilsondev.in/sou-um-procrastinador-e-hoje-estou-bem-com-isso</guid><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Tue, 01 Nov 2022 13:49:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1667310456223/JMZaMDHmS.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>Antes de começar: </strong>Esse post eu criei em 2017 no Medium, mas ele continua atual, ainda mais em relação com o meu recente diagnóstico de que tenho TDAH e sinais leves de TEA (Transtorno do Expectro Autista). Eu ainda estou assimilando tudo isso, mas no futuro quero escrever mais sobre. Até lá, espero que esse post traga uma boa reflexão desse mundo que exige de nós uma alta performance a todo momento.</p>
</blockquote>
<p>Na vida, sempre temos algo que nos angustia, nos chateia, e que queremos superar ou melhorar, e o que me deixa mais chateado é a minha atitude em procrastinar as coisas, o clássico “empurrando com a barriga”. Mas hoje não fico tão triste com isso porque vejo que isso é algo que vem de mim. Isso prejudica com certeza, mas como tudo na vida, temos que pegar o lado bom de tudo.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667309553738/HcE4nZv6x.png" alt="image.png" class="image--center mx-auto" /></p>
<blockquote>
<p>Se a vida te der um limão, faça uma limonada!</p>
</blockquote>
<p>Quem me conhece, sabe que uso qualquer tipo de ideia ou ferramenta que me ajude a ser mais produtivo. Um tempo usei o <a target="_blank" href="https://pt.wikipedia.org/wiki/T%C3%A9cnica_pomodoro">pomodoro</a>, que é um ótimo timebox para estudos, e qualquer tipo de tarefa que precise de foco, e hoje uso o <a target="_blank" href="https://pt.wikipedia.org/wiki/Getting_Things_Done">GTD</a> para armazenar e controlar todas as tarefas do meu cotidiano. Para quem consegue finalizar <strong>todas</strong> as suas tarefas que aparecem, pode achar isso loucura, mas eles me salvam tem 4 anos. Mas como nada é uma bala de prata, tenho recaídas, e elas geram desânimo e até breves momentos de depressão. E para quem tem um certo nível de TDAH, sabem que essas coisas intensificam, por mais banais que sejam.</p>
<p>Fazendo minhas leituras entre livros de História, Programação e Política, esses dias acabei lendo um livro chamado <a target="_blank" href="http://www.saraiva.com.br/a-arte-da-procrastinacao-como-realizar-tarefas-deixando-as-para-depois-7677292.html">A Arte da Procrastinação — Como Realizar Tarefas Deixando-as Para Depois</a> porque sempre gosto de encontrar algo que possa melhorar meu desempenho. Achei o título interessante e fiquei meio desconfiado, mas comecei a ler. O interessante é que tive uma surpresa meio óbvia mas que não nos tocamos: <strong>não sou o único</strong>. É idiota pensar nisso, mas por favor, todo mundo tem esse questionamento com qualquer defeito que encontra em você mesmo.</p>
<p>Ele trouxe alguns pontos que todo procrastinador vai se identificar. Quero descrever alguns deles.</p>
<h2 id="heading-muitas-tarefas-sendo-feitas-mas-suas-prioridades-sao-questionaveis">Muitas tarefas sendo feitas, mas suas prioridades são questionáveis</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667309711947/Afmf7D8B2.png" alt="image.png" class="image--center mx-auto" /></p>
<p>Tarefas sempre vão aparecer e serão várias, mas conseguimos fazer muitas delas. Mas será que realmente era necessário finalizá-las agora?</p>
<blockquote>
<p>Meu fracasso mais sério, em termos desse ideal, é a procrastinação. Em 1995, por não trabalhar em algum projeto ao qual deveria estar me dedicando, comecei a me sentir mal. Mas aí notei algo. No geral, eu tinha a reputação de alguém que fazia muitas coisas e contribuía de forma razoável com a Universidade de Stanford, onde eu trabalhava, e com a disciplina de filosofia, que era meu campo de ensino. Um paradoxo. — Perry, John</p>
</blockquote>
<p>Quantas vezes ficamos nessa situação? Muitos projetos na cabeça, mas nenhum concluído, mas por outro lado, outros tipos de trabalhos fazemos. O John definiu como um <strong>procrastinador estruturado.</strong></p>
<blockquote>
<p>Entendi que eu mesmo era o que chamei de <em>procrastinador estruturado</em>: uma pessoa que faz muito ao não fazer outras coisas. — Perry, John</p>
</blockquote>
<p>Ou seja, somos profissionais em fazermos muitas tarefas, mas nos deparamos com o fato de que não eram realmente importantes, que não estavam na categoria da prioridade máxima. O mais interessante é que o livro não traz métodos para buscarmos o nosso melhor para acabarmos com isso, coisa que existem em muitas outras obras por ai, mas termos atitudes que possamos <strong>conviver e usar esse problema ao nosso favor,</strong> fazendo com que a coisa mude de figura.</p>
<blockquote>
<p>Todos os procrastinadores adiam as coisas que precisam fazer. A procrastinação estruturada é a arte de fazer esse traço negativo trabalhar por você. A ideia central é que a procrastinação não significa que você não vai fazer absolutamente nada.</p>
<p>Procrastinadores raramente não fazem absolutamente nada; eles fazem coisas marginalmente úteis, como jardinagem, apontar lápis ou criar um diagrama de como vão reorganizar seus arquivos quando se decidirem a iniciar. Por que o procrastinador faz essas coisas? Porque são uma forma de não fazer algo mais importante.</p>
<p>Se tudo que o procrastinador tivesse de fazer fosse apontar lápis, nenhuma força na Terra o obrigaria a fazer isso. O procrastinador pode ser motivado a fazer tarefas difíceis, convenientes e importantes, desde que essas tarefas sejam uma forma de não fazer algo ainda mais importante. — Perry, John</p>
</blockquote>
<p>Ele mostra que com a procrastinação estruturada, podemos moldar a forma que definimos as nossas tarefas “para fazer de uma forma que se explore esse fato”. Tentamos minimizar os nossos compromissos ao básico, pensando que vamos conseguir fazê-los. Pode funcionar por um momento, mas depois vem aquela decaída novamente, que é a nossa <strong>autosabotagem</strong>. Ele fica pairando sobre você, enquanto consegue um maravilhoso momento em que está produzindo, sendo útil para a sociedade, mas qualquer descuido, ele nos ataca. Volta todo o sentimento de inutilidade e desânimo. Entenda, ele vai estar sempre ao lado querendo acabar com seu desempenho, mas precisamos aprender a usar a nosso favor.</p>
<blockquote>
<p>O truque é pegar o tipo certo de projetos para pôr no alto da lista. A espécie ideal de tarefas tem duas características. Primeiro, elas parecem ter prazos claros (mas realmente não é assim). Segundo, parecem incrivelmente importantes (mas realmente não são). Felizmente, a vida está cheia de tarefas assim. — Perry, John</p>
</blockquote>
<p>E é verdade. Quem nunca retomou pela enésima vez para sua lista de afazeres, e depois queremos definir suas prioridades, mas com o tempo acaba que todos são urgentes? Por mais que ninguém aceite, nem toda tarefa vai ter sempre um prazo claro e vai ser importante. Para nós são tarefas e pronto. Mas o que fazemos quando vier o <em>sentimento de fazer para depois</em>?</p>
<h2 id="heading-preencha-o-fazer-a-tarefa-para-depois-colocando-outra-tarefa">Preencha o “fazer a tarefa para depois”, colocando outra tarefa</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667309922596/uj5uqj1eU.png" alt="image.png" class="image--center mx-auto" /></p>
<blockquote>
<p>Sempre faça uma tarefa</p>
</blockquote>
<p>Sim, é isso mesmo. Quando você olha para uma tarefa, e bate aquele incômodo de fazer e em sequência vem o desejo de fazer depois, <strong>procure outra tarefa na sua lista diária e faça</strong>. Isso evita o problema de criar tempo ocioso aonde não pode ter, e não fica com aquele sentimento que não fez nada no dia.</p>
<h2 id="heading-se-encontrou-tarefas-que-so-aumentam-o-desejo-de-adia-las-quebre-em-pequenas-partes">Se encontrou tarefas que só aumentam o desejo de adiá-las, quebre em pequenas partes</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667309966029/y2bcapH4_.png" alt="image.png" class="image--center mx-auto" /></p>
<blockquote>
<p>Faça em partes</p>
</blockquote>
<p>Dividir para conquistar ou dividir para reinar, já dizia César. Se uma tarefa que definiu é assustador suficiente para desanimar, quebre em pequenos passos. Assim, alivia o sentimento de adiamento, focando um item de cada vez (dica: use o pomodoro para focar em cada passo), que no final do dia vai ver o seu progresso e vai se sentir feliz com isso.</p>
<h2 id="heading-o-perfeccionismo-e-o-amigo-da-procrastinacao">O perfeccionismo é o amigo da procrastinação</h2>
<p>Não quer dizer que você não pode buscar a fazer o melhor, mas o que estou dizendo é <strong>simplesmente faça</strong>. Porque o perfeccionismo que temos, é do tipo fantasioso.</p>
<blockquote>
<p>Acho que perfeccionismo leva à procrastinação. Demorei para ver a conexão entre os dois, porque não me vejo como perfeccionista. Muitos procrastinadores não percebem que são perfeccionistas, pela simples razão de que nunca fizemos nada com perfeição, não chegamos nem perto disso. — Perry, John</p>
</blockquote>
<p>Eu sou perfeccionista em muitas coisas que faço, e depois de fazer uma auto-análise, vejo que acabo perdendo tempo pensando nas melhores formas de fazer algo, do que simplesmente começar, e depois melhorando com o tempo. É o que meu colega <a target="_blank" href="http://henriquebastos.net/">Henrique Bastos</a> já falou em um curso de desenvolvimento de sistemas com <a target="_blank" href="http://welcometothedjango.com.br/">Python e Django</a> que fiz com ele. Ele chamou isso de <strong>síndrome do astronauta</strong>.</p>
<blockquote>
<p>Nós programadores temos ou tivemos o síndrome do astronauta. Ficamos pensando na melhor solução, planejando e quando vemos estamos no mundo da lua. Bastos, Henrique (com modificações)</p>
</blockquote>
<p>E vejo que isso não se limita a minha área, mas a todos os procrastinadores. Então controle o seu perfeccionismo, faça o que precisa ser feito mesmo não estando do jeito que gostaria. Depois, analise o que criou e melhore. Recomendo que estudem sobre <a target="_blank" href="http://www.infoescola.com/sociedade/kaizen/">Kaizen</a>.</p>
<h2 id="heading-nunca-desista-das-suas-listas-de-tarefas-mesmo">Nunca desista das suas listas de tarefas. Mesmo!</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667310049706/zQH_xdDvJ.png" alt="image.png" class="image--center mx-auto" /></p>
<blockquote>
<p>Tenha uma lista colado em você.</p>
</blockquote>
<p>Falamos de como podemos lidar com as tarefas, mas vamos sempre precisar de listas. Não confie na sua mente, um hora aquele pedido que recebeu do seu chefe vai ficar entulhado no emaranhado de idéias que sempre surge na sua mente, então procure uma melhor abordagem para você. Eu hoje uso o <a target="_blank" href="https://pt.wikipedia.org/wiki/Getting_Things_Done">GTD (Getting Things Done)</a> idealizado por David Allen, e recomendo que faça uma breve leitura sobre ela. Mesmo que você não use a metodologia 100% (coisa que ela não exige, pode começar aos poucos), ela pode trazer bons insights para o seu worfklow.</p>
<p>O importante é que ela esteja sempre com você, e a cada compromisso que venha ao seu encontro, coloque nela. Não se preocupe caso acabar esquecendo da lista, mas quando lembrar, pegue-a e tente novamente até não conseguir mais ficar sem ela.</p>
<h2 id="heading-respeite-o-seu-ritmo">Respeite o seu ritmo</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667310082379/8RKoqXj6x.png" alt="image.png" class="image--center mx-auto" /></p>
<blockquote>
<p>Cada um tem o seu ritmo, saiba o seu e respeite-a.</p>
</blockquote>
<p>Todos são diferentes, inclusive o ritmo em produzir algo. As vezes queremos acelerar para fazer o máximo possível em menos tempo, e isso para um procrastinador é um veneno, <strong>no início</strong>. Antes faça no seu ritmo, porque caso forçar demais poderá acabar abandonando o que estava fazendo. Quando sentir que pode aumentar sua velocidade, faça. As vezes é melhor fazer algo no seu tempo e terminá-la, do que fazer ligeiro e ficar pela metade.</p>
<h2 id="heading-use-o-computador-e-a-internet-com-sabedoria">Use o computador e a internet com sabedoria</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667310220803/vnBuLLcTe.png" alt="image.png" class="image--center mx-auto" /></p>
<blockquote>
<p>Computador e Internet veio para ajudar, e não atrapalhar.</p>
</blockquote>
<p>Talvez para alguns não é um problema, mas para mim é uma das grandes. Eu desenvolvo sistemas, e o computador e a internet são ferramentas essenciais, para consulta, estudo, download, etc. E preciso tomar o cuidado de não cair na tentação de acessar e-mails além do normal e redes sociais, e pior, durante a pesquisa entrar no caminho infinito de links dentro de links, que no final absorvi informação além do que preciso.</p>
<p>Para sanar esse problema, acabo usando ferramentas em navegadores para bloquear certos sites durante momentos em que preciso de foco. Para quem usa o Chrome recomendo a extensão <a target="_blank" href="https://chrome.google.com/webstore/detail/stayfocusd/laankejkbhbdhmipfmgcngdelahlfoji">Stay Focusd</a> que você pode configurar o intervalo de tempo e quais sites deseja bloquear.</p>
<p>Outro que gosto de usar, é o <a target="_blank" href="https://www.rescuetime.com/">Rescue Time</a> que é um serviço que monitora o tempo que gasta em cada aplicação no seu desktop, como também em cada site na web. Você pode classificar cada um deles como produtivo ou não, e depois recebe por e-mail um relatório semanal do tempo gasto e quanto tempo foi produtivo ou não. Acho legal, porque vejo quais sites que não estão na lista e atualizo. São ferramentas que vão ajudar, mas o mais importante é você fazer sua parte.</p>
<p><strong>Atualizado (11/01/20):</strong> Depois de alguns anos depois desse post, eu e toda a Sociedade passou por situações no mínimos bizarras na Internet, principalmente nas Redes Sociais, que tornou a discussão sobre política e tudo mais em um extremo cada vez pior, graças as bolhas que vivemos. Diante dessa selva digital, precisamos nos condicionar a ter um comportamento adequado não só no mundo real. Mais uma vez o Henrique Bastos, trouxe alguns insights necessários hoje e sempre sobre como levdar com essa selva binária:</p>
<p><img src="https://miro.medium.com/max/1200/1*4kNfS-jg2XCl3ev6ZzEbmQ.jpeg" alt />
<a target="_blank" href="https://medium.com/@henriquebastos/12-regras-para-sobreviver-na-selva-da-internet-a8b5f9f5739a">12 regras para sobreviver na selva da Internet</a></p>
<p>A internet eliminou as distâncias, mas ainda não sabemos superar o ruído. Descubra como sobreviver e aproveitar o que a rede tem de melhor.</p>
<h2 id="heading-fique-ao-lado-de-pessoas-que-te-ajudem-a-crescer">Fique ao lado de pessoas que te ajudem a crescer</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667310297904/QvdjIH8nT.png" alt="image.png" class="image--center mx-auto" /></p>
<blockquote>
<p>Sempre temos alguém que nos quer bem</p>
</blockquote>
<p>Procrastinadores que recebem apoio de pessoas que querem o seu bem, é sempre importante. Elas ajudam a criarmos um “compromisso público” de que temos que fazer algo. Já trabalhei em projetos com alguém que um vigiava o outro nas tarefas que fazíamos (de forma saudável claro, sem exageros), e que trouxe ganhos. Assim, se puder contar com algum amigo, colega de trabalho ou alguém da família para acompanhar na sua jornada, junte-se a eles e vai ver os frutos disso.</p>
<h2 id="heading-se-alguma-tarefa-pode-ser-feita-por-outra-pessoa-delegue-a-na-medida-do-possivel">Se alguma tarefa pode ser feita por outra pessoa, delegue-a na medida do possível</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667310340467/9bQpyuy-7.png" alt="image.png" class="image--center mx-auto" /></p>
<blockquote>
<p>Delegue, mas com bom senso.</p>
</blockquote>
<p>Aqui é preciso um bom senso. Não quero dizer que deve sempre jogar suas responsabilidades para os outros, mas se alguém pode ajudar você a terminar alguma tarefa, e ela tenha capacidade de fazer isso sozinha sem a comprometer, delegue. Isso vai ajudar a tornar sua lista diária menos, e consequentemente vai esvaziá-la no fim do dia. Por outro lado, sempre acompanhe a pessoa que te ajudou, caso precise de algo, porque como falei é uma questão de bom senso.</p>
<h2 id="heading-procrastinadores-irritam-outras-pessoas-entao-mostre-para-elas-como-e-dificil-ser-assim">Procrastinadores irritam outras pessoas, então mostre para elas como é difícil ser assim</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667310356279/iJywPqAvY.png" alt="image.png" class="image--center mx-auto" /></p>
<blockquote>
<p>Mostre sua visão de mundo para outras pessoas.</p>
</blockquote>
<p>Eu sou casado, e minha esposa precisa de muita paciência comigo, porque acabo esquecendo de fazer o que precisa, não presto atenção algumas das vezes porque uma idéia surgiu na minha cabeça na hora, e quando vai ver estou sendo puxado de volta com uma reclamação dela. Temos que ter a consciência que nós irritamos pessoas que não são procrastinadoras, seja esposa ou marido, familiares, colegas de trabalho, etc. Mas o que eles não sabem é que isso nos entristece. Ficamos chateados quando esquecemos ou não fazemos algo que foi pedido, ou quando perdemos o prazo e que isso tudo está prejudicando alguém. Quando acontece, volta o momento desmotivação. Quantas vezes em final de ano vem aquele desapontamento de que não conquistou praticamente nada?</p>
<p>Então, converse e mostre o seu lado nisso tudo. Eu sempre converso com as pessoas que convivo sobre o meu problema, e como procuro melhorar para que isso não afete ao meu redor. Mostre que mesmo quando as deixamos estressadas, não é nossa intenção, e que buscamos o máximo para consertar e evitar isso. Assim, em algum momento vão entender e talvez possam refletir nisso, para que caso isso aconteça possam ter um pouco de paciência e ajudar a sairmos do outro lado. Então, converse e procurem entrar em consenso na medida do possível.</p>
<h2 id="heading-desistir-e-natural-mas-continuar-na-desistencia-e-burrice">Desistir é natural, mas continuar na desistência é burrice</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1667310404870/yWgX24kxI.png" alt="image.png" class="image--center mx-auto" /></p>
<blockquote>
<p>Just do it!</p>
</blockquote>
<p>Sempre vai vir momentos em que vamos desistir em lidar com a procrastinação, é comigo e vai acontecer com você. Mas o que não devemos deixar, é que isso continue. Então, volte e repense no que fez, procure ser melhor um dia de cada vez.</p>
<p>Somos procrastinadores sim, mas podemos lidar com isso e vivermos bem. Bom deixa eu voltar na tarefa que deixei para escrever esse artigo. Até mais!</p>
]]></content:encoded></item><item><title><![CDATA[Substituindo textos em arquivo com o comando sed]]></title><description><![CDATA[O comando sed é um programa de linha de comando que oferece um modo de parsing de texto e transformação orientado a linhas. Ele se utiliza de uma sintaxe dedicada e também tem suporte a Expressões Regulares. Uma das aplicações é substituir algum text...]]></description><link>https://blog.gilsondev.in/substituindo-textos-em-arquivo-com-o-comando-sed</link><guid isPermaLink="true">https://blog.gilsondev.in/substituindo-textos-em-arquivo-com-o-comando-sed</guid><category><![CDATA[sed]]></category><category><![CDATA[Linux]]></category><category><![CDATA[linux-commands]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Tue, 25 Oct 2022 18:26:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/IhWYiwSxm8g/upload/v1666722352167/PCeJfrHz7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>O comando <a target="_blank" href="https://www.gnu.org/software/sed/manual/sed.html">sed</a> é um programa de linha de comando que oferece um modo de <em>parsing</em> de texto e transformação orientado a linhas. Ele se utiliza de uma sintaxe dedicada e também tem suporte a Expressões Regulares. Uma das aplicações é substituir algum texto de um arquivo.</p>
<p>Vamos supor que temos um um arquivo <code>input.txt</code> em que possui no seu texto uma palavra <code>hello</code>. O que queremos é substituir para <code>world</code>. Segue o exemplo do conteúdo:</p>
<pre><code class="lang-plain">Hi,

My name is Gilson and Hello There!
And hello to his family.
</code></pre>
<p>E para alterar isso podemos utilizar a seguinte sintaxe:</p>
<pre><code class="lang-bash">sed SCRIPT INPUTFILE...
</code></pre>
<p>Em que <code>SCRIPT</code> é o local que o programa vai executar as transformações com base no seu sintaxe e também via RegeX.</p>
<p>Com isso podemos utilizar a sintaxe interna do programa com o <code>s/&lt;old&gt;/&lt;new&gt;/[&lt;flag&gt;, ...]</code> para alterar <code>hello</code> para <code>world</code>:</p>
<pre><code class="lang-bash">sed <span class="hljs-string">'s/hello/world/'</span> input.txt
</code></pre>
<p>Usando dessa forma, por padrão o <code>sed</code> irá utilizar a saída padrão para trazer o resultado da substituição:</p>
<pre><code class="lang-bash">sed <span class="hljs-string">'s/hello/world/'</span> input.txt                                                                                                                                                                                                                                                                                                                                          
Hi,

My name is Gilson and Hello There!
And world to his family.
</code></pre>
<p>Caso precisa fazer a alteração no próprio arquivo e por todas as ocorrências encontradas, podemos utilizar algumas funções adicionais:</p>
<ul>
<li>Argumento <code>-i</code>: utilizado para aplicar uma mudança <em>in-place</em>, sem saída padrão.</li>
<li>Flag <code>g</code> do comando <code>s//</code>: utilizado para aplicar a mudança em todas as ocorrências encontradas no conteúdo</li>
<li>Flag <code>i</code> do comando <code>s//</code>: utilizado para aplicar a mudança independente de maiúscula ou minúscula (conhecido como <em>case-insensitive</em>).</li>
</ul>
<pre><code class="lang-bash">sed -i <span class="hljs-string">'s/hello/world/gi'</span> input.txt
</code></pre>
<pre><code class="lang-bash">cat input.txt
Hi,

My name is Gilson and world There!
And world to his family.
</code></pre>
<p>Caso queira testar os parâmetros e comandos disponíveis em um modo <em>live preview</em> existe uma ferramenta interessante para verificar se a sintaxe utilizada no sed está correta, é o <a target="_blank" href="https://sed.js.org/">GNU sed REPL</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1666722317619/3puqAc_GY.png" alt="image.png" /></p>
]]></content:encoded></item><item><title><![CDATA[Cruzando dados entre Cloud SQL e BigQuery com as Queries Federadas]]></title><description><![CDATA[No Google Cloud Platform possuem várias formas de armazenar dados, mas pode ter situações em que é preciso pegar dados de uma base relacional e cruzar com o que está salvo no BQ para suas análises nos dados. Uma forma de fazer é o uso de Queries Fede...]]></description><link>https://blog.gilsondev.in/cruzando-dados-entre-cloud-sql-e-bigquery-com-as-queries-federadas</link><guid isPermaLink="true">https://blog.gilsondev.in/cruzando-dados-entre-cloud-sql-e-bigquery-com-as-queries-federadas</guid><category><![CDATA[bigquery]]></category><category><![CDATA[cloud sql]]></category><category><![CDATA[dataengineering]]></category><category><![CDATA[big data]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Fri, 23 Sep 2022 21:09:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/T9rKvI3N0NM/upload/v1663967295216/kAKhmy9B9.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>No Google Cloud Platform possuem várias formas de armazenar dados, mas pode ter situações em que é preciso pegar dados de uma base relacional e cruzar com o que está salvo no BQ para suas análises nos dados. Uma forma de fazer é o uso de <strong>Queries Federadas</strong>, em que é feito uma consulta em um banco de dados no Cloud SQL e um dataset no BigQuery, sem a necessidade de copiar ou mover dados utilizando algum armazenamento intermediário como o Goocle Cloud Storage no processo, por exemplo. Esse procedimento é suportado em instâncias que utilizam MySQL ou PostgreSQL.</p>
<p>Mas antes vamos precisar seguir algumas configurações necessárias para fazer esse procedimento como:</p>
<ul>
<li><a target="_blank" href="https://cloud.google.com/bigquery/docs/cloud-sql-federated-queries#enable_the_connection_service">Habilitar o uso do BigQuery</a></li>
<li><a target="_blank" href="https://cloud.google.com/bigquery/docs/cloud-sql-federated-queries#public_ip">Habilitar o IP público da sua instância Cloud SQL</a></li>
<li><a target="_blank" href="https://cloud.google.com/bigquery/docs/cloud-sql-federated-queries#setting-up-cloud-sql-database-connections">Criar uma conexão externa do Cloud SQL no BigQuery</a></li>
<li><a target="_blank" href="https://cloud.google.com/bigquery/docs/cloud-sql-federated-queries#access-sql">Garantir que o seu usuário ou service account tenha as roles adequadas</a></li>
</ul>
<h2 id="heading-exemplos-de-uso">Exemplos de uso</h2>
<p>Com as queries federadas você utilizar para dois tipos de necessidades que vamos exemplificar: <strong>cruzar dados de bases diferentes</strong> e <strong>efetuar ingestão de dados</strong>.</p>
<h3 id="heading-exemplo-1-cruzando-dados-de-bases-diferentes">Exemplo 1: Cruzando dados de bases diferentes</h3>
<p>Imagine que você tem dois serviços independentes, focados em dados de clientes e vendas respectivamente. Cada um deles armazena em uma base diferente. Precisamos fazer uma busca em que queremos saber qual foi a data da primeira compra feita por cada cliente.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663966767398/scIigeu7x.png" alt="image.png" /></p>
<p>Essa query pode ser feita no BigQuery e utiliza a função <a target="_blank" href="https://cloud.google.com/bigquery/docs/reference/standard-sql/federated_query_functions#external_query">EXTERNAL_QUERY</a>. A consulta abaixo mostra como isso é feito:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> c.customer_id, c.name, rq.first_order_date
<span class="hljs-keyword">FROM</span> mydataset.customers <span class="hljs-keyword">AS</span> c
<span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">OUTER</span> <span class="hljs-keyword">JOIN</span> EXTERNAL_QUERY(
  <span class="hljs-string">'us.orders_db'</span>,
  <span class="hljs-string">'''SELECT customer_id, MIN(order_date) AS first_order_date
    FROM orders  
    GROUP BY customer_id'''</span>) <span class="hljs-keyword">AS</span> rq <span class="hljs-keyword">ON</span> rq.customer_id = c.customer_id
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> c.customer_id, c.name, rq.first_order_date;
</code></pre>
<p>Nesse trecho temos o seguinte:</p>
<ul>
<li>O primeiro argumento da função é o ID da conexão externa que foi criada no BQ, e isso é importante para ter acesso a base</li>
<li>O segundo argumento é a query SQL que será executada no banco externo, respeitando toda a sintaxe do fornecedor (como PostgreSQL por exemplo). Os dados retornados serão incluídos em uma tabela temporária.</li>
<li>Com base na tabela temporária é feito um JOIN, utilizando o campo <code>customer_id</code></li>
<li>E por fim a consulta principal faz a seleção dos dados e os agrupa.</li>
</ul>
<p>Dessa forma você consegue extrair informações que precisa, sem ter que ficar copiando um grande volume.</p>
<h3 id="heading-exemplo-2-efetuando-ingestao-de-dados">Exemplo 2: Efetuando ingestão de dados</h3>
<p>Seguindo o mesmo cenário do primeiro exemplo, vamos supor que agora queremos fazer a ingestão de todos os dados da tabela <code>orders</code>, como fazer nesse caso? A query abaixo mostra como fazer:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> <span class="hljs-string">`foobar-project.ecommerce.orders`</span>
<span class="hljs-keyword">SELECT</span> orders.* <span class="hljs-keyword">FROM</span> EXTERNAL_QUERY(
  <span class="hljs-string">"us.orders_db"</span>,
  <span class="hljs-string">'''SELECT * FROM orders;'''</span>

) orders;
</code></pre>
<p>Aqui é mais simples de entender, mas basicamente o resultado da query feita na tabela <code>orders</code> da base externa, vai para a tabela temporária e com isso as informações vão servir como insumo para a ingestão dos dados na tabela <code>orders</code> do dataset <code>ecommerce</code> no BQ.</p>
<p>Aqui temos algumas considerações a colocar no uso desse cenário:</p>
<ul>
<li>Ao utilizar o asterisco (<code>*</code>) na consulta, o nome das colunas da tabela BQ precisam ser idênticas a tabela externa. Caso contrário defina os nomes de forma explícita (nas duas entidades de preferência)</li>
<li>A ingestão é feita se os dados externos respeitarem os tipos de dados que as colunas da tabela BQ foram declaradas. Na documentação existe uma <a target="_blank" href="https://cloud.google.com/bigquery/docs/reference/standard-sql/federated_query_functions#data_type_mappings">tabela de mapeamento dos dados</a>, que explica melhor.</li>
</ul>
<p>Em ambos os casos, o uso das Queries Federadas podem ser de grande utilidade para quem lida com dados no dia a dia.</p>
]]></content:encoded></item><item><title><![CDATA[Consultando uma amostragem de dados em tabelas no BigQuery]]></title><description><![CDATA[Quando trabalhamos com o BigQuery precisamos construir as queries para efetuar consultas em uma massa de dados que normalmente é muito grande, então para quem já trabalhou com SQL sabe que uma forma de retornar uma amostra pequena dos dados é utiliza...]]></description><link>https://blog.gilsondev.in/consultando-uma-amostragem-de-dados-em-tabelas-no-bigquery</link><guid isPermaLink="true">https://blog.gilsondev.in/consultando-uma-amostragem-de-dados-em-tabelas-no-bigquery</guid><category><![CDATA[bigquery]]></category><category><![CDATA[BigData]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Thu, 22 Sep 2022 20:05:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/Wpnoqo2plFA/upload/v1663877076897/BB98ULALG.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Quando trabalhamos com o BigQuery precisamos construir as queries para efetuar consultas em uma massa de dados que normalmente é muito grande, então para quem já trabalhou com SQL sabe que uma forma de retornar uma amostra pequena dos dados é utilizar a palavra <code>LIMIT &lt;quantidade de registros&gt;</code> vindo em seguida com a quantidade que precisa retornar. O Google trabalha sua cobrança nas consultas a serem feitas, definido pelo tamanho de dados (MB, Gb, Tb, etc) que vai precisar percorrer para cumprir o que foi pedido.</p>
<p>Infelizmente se fizermos a consulta abaixo em que queremos retornar 10 registros em uma base de 1Tb por exemplo não vai economizar na cobrança:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> dataset.table1 <span class="hljs-keyword">LIMIT</span> <span class="hljs-number">10</span>
</code></pre>
<p>Seguindo a mesma idéia, usando uma base pública do BigQuery, que é os dados dos aluguéis de bicicletas em Nova York. A query abaixo traz pouca informação, mas olha a quanidade de dados que ele vai precisar processar para executar:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663876731670/wMxUkYDld.png" alt="image.png" /></p>
<p>Para ficar claro que o <code>LIMIT</code> nesse contexto não funciona vamos fazer o seguinte: vamos selecionar  <strong>todas as colunas</strong> da tabela:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663876778410/rHN-Q6hVX.png" alt="image.png" /></p>
<p>Aumentou para <strong>7.5 GiB</strong>!</p>
<p>Ou seja, mesmo que solicitamos somente uma pequena amostra, a cobrança vai ser dentro dos dados que ele vai percorrer. Se a ferramenta disse que precisa passar por 800Gb por exemplo, ele vai fazer isso mesmo assim. Isso é ruim porque quanto mais fazer suas consultas e elas exigirem mais dados para percorrer, mais caro vai ficar no final do mês. O BigQuery tem algumas formas de resolver isso.</p>
<h2 id="heading-consultas-em-tabelas-particionadas">Consultas em tabelas particionadas</h2>
<p>Uma forma é fazer o <a target="_blank" href="https://cloud.google.com/bigquery/docs/querying-partitioned-tables">particionamento das tabelas</a>, baseado em alguma informação como a data de registro de um produto como <code>_PARTITIONDATE</code> em que a plataforma, ou a partir dos dados de uma coluna da tabela. Assim você pode escolher por exemplo uma data em que tem uma quantidade pequena de dados para criar suas consultas:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> dataset.table1 <span class="hljs-keyword">WHERE</span> _PARTITIONDATE = <span class="hljs-string">'2021-01-01'</span>
</code></pre>
<p>Dessa forma, o particionamento pode ajudar inclusive em produção para tornar suas consultas mais baratas e rápidas.</p>
<h2 id="heading-consultas-em-amostragem">Consultas em amostragem</h2>
<p>Uma segunda forma é fazer uma consulta em cima de uma seleção menor, um subconjunto de dados através do <code>TABLESAMPLE</code>. Ele traz uma quantidade aleatória de dados de uma tabela a partir de um determinada porcentagem. Por exemplo, em uma tabela de 10.000 registros, eu quero consultar em uma amostra de 1%, ou seja, 100 registros:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> dataset.table1 <span class="hljs-keyword">TABLESAMPLE</span> <span class="hljs-keyword">SYSTEM</span>(<span class="hljs-number">1</span> <span class="hljs-keyword">PERCENT</span>)
</code></pre>
<p>Dessa forma ele atua algo como um <code>LIMIT</code> nos bancos relacionais, mas principalmente diminuir os custos fazendo com que a plataforma percorra por menos dados. No nosso exemplo da base pública, se definirmos uma amostra de 1% em cima dos <strong>7.5 GiB de dados</strong> diminuimos para <strong>80.5 MB</strong> na consulta e com isso fica mais barato.</p>
<h3 id="heading-consulta-em-uma-amostra-de-uma-amostra">Consulta em uma amostra de uma amostra</h3>
<p>Além disso você pode consultar em uma amostra menor ainda, a partir de outra. Por exemplo, você deseja ler 20% dos blocos de dados armazenados e quer selecionar 10% das linhas desses blocos:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> dataset.table1 <span class="hljs-keyword">TABLESAMPLE</span> <span class="hljs-keyword">SYSTEM</span> (<span class="hljs-number">20</span> <span class="hljs-keyword">PERCENT</span>) <span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">rand</span>() &lt; <span class="hljs-number">0.1</span>
</code></pre>
<hr />
<h4 id="heading-referencias">Referências</h4>
<ul>
<li>Table sampling. BigQuery Documentation. Disponível em: https://cloud.google.com/bigquery/docs/table-sampling. Acesso em: 22/09/2022</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Acessando suas instâncias Cloud SQL com  SQL Auth Proxy]]></title><description><![CDATA[Esse serviço oferece um meio seguro de acessar suas instâncias do Cloud SQL sem precisar de redes autorizadas ou mesmo configurar SSL para fazer conexão ao banco de dados. Ele oferece algumas vantagens:

Conexões seguras: o Auth Proxy automaticamente...]]></description><link>https://blog.gilsondev.in/acessando-suas-instancias-cloud-sql-com-sql-auth-proxy</link><guid isPermaLink="true">https://blog.gilsondev.in/acessando-suas-instancias-cloud-sql-com-sql-auth-proxy</guid><category><![CDATA[cloud sql]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Wed, 21 Sep 2022 12:00:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/XIIsv6AshJY/upload/v1663707116794/RYjhuG3zP.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Esse serviço oferece um meio seguro de acessar suas instâncias do Cloud SQL sem precisar de <a target="_blank" href="https://cloud.google.com/sql/docs/postgres/configure-ip">redes autorizadas</a> ou mesmo <a target="_blank" href="https://cloud.google.com/sql/docs/postgres/configure-ssl-instance">configurar SSL</a> para fazer conexão ao banco de dados. Ele oferece algumas vantagens:</p>
<ul>
<li><strong>Conexões seguras</strong>: o Auth Proxy automaticamente encripta todo o tráfego de dados de ponta a ponta utilizando TLS 1.3 com 256-bit <em>AES cipher</em>. Em toda a comunicação é utilizado certificados SSL para verificar a identidade do <em>client</em> e do <em>server</em>, independente do protocolo do banco de dados, fora que não precisa gerenciá-los.</li>
<li><strong>Autorização de conexão facilitada</strong>: é utilizado as permissões <em>IAM</em> para controlar quem e quando conectar na sua instância. Além disso o Auth Proxy lida com a autenticação com o Cloud SQL, de forma que remove a necessidade  de prover um endereço de IP estático.</li>
<li><strong>Autenticação do banco via IAM</strong>: opcionalmente o Auth Proxy suporta a opção de atualização automática de tokens <em>OAuth 2.0</em></li>
</ul>
<p>O Auth Proxy não fornece um novo caminho de conectividade, então ele depende da conectividade existente. Para conectar utilizando o IP privado, é preciso estar em um recurso com acesso à mesma rede VPC da instância.</p>
<h2 id="heading-requisitos-de-utilizacao">Requisitos de utilização</h2>
<p>Para fazer uso dela precisa seguir algumas premissas:</p>
<ul>
<li>Precisa ter o <em>Cloud SQL Admin API</em> habilitado</li>
<li>Prover para o Auth Proxy credenciais de autenticação do GCP</li>
<li>Prover para o Auth Proxy as contas de usuário e senhas do banco válidos</li>
<li>A instância deve ter um IP público IPv4 ou ser configurado para utilizar o IP privado</li>
</ul>
<p>Para fazer a instalação, <a target="_blank" href="https://cloud.google.com/sql/docs/postgres/sql-proxy#install">siga as instruções da documentação</a>.</p>
<h2 id="heading-como-o-cloud-sql-auth-proxy-funciona">Como o Cloud SQL Auth Proxy funciona?</h2>
<p>O serviço segue alguns passos para chegar no objetivo que é disponibilizar uma conexão disponível no banco:</p>
<ol>
<li>Começando por um <em>client</em> local executando no mesmo ambiente da aplicação, por exemplo. Ela comunica com o Auth Proxy através do protocolo do banco de dados, como seria comunicado diretamente com a base de dados.</li>
<li>Em seguida, o Auth Proxy usa um túnel seguro para comunicar com o processo semelhante sendo executado no servidor. Cada conexão estabelecida através do Auth Proxy cria uma conexão para a instância Cloud SQL.</li>
<li>Quando essa aplicação conecta através do Auth Proxy, o <em>client</em> local verifica se já possui uma conexão disponível entre ela, e a instância Cloud SQL. Caso contrário, é solicitado uma nova, gerando inclusive certificados SSL efêmeros e assim utilizados nessa comunicação</li>
</ol>
<p>Abaixo a imagem traz uma visão geral de tudo isso:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663707184387/wkFiFdM8Q.png" alt="Pasted image 20220322125514.png" /></p>
<blockquote>
<p><strong>Observação:</strong> Esse certificados efêmeros expiram em aproximadamente 1 hora, mas o Auth Proxy tem a inteligência de solicitar uma renovação antes disso acontecer.</p>
</blockquote>
<h2 id="heading-autenticacao-via-service-account">Autenticação via Service Account</h2>
<p>O Cloud SQL Auth Proxy se utiliza dos Service Accounts para autorizar a conexão entre o <em>client</em> e a instância. Dessa forma, tenha disponível essa chave e passe via parâmetro para o <code>cloud_sql_proxy</code>. Quando for utilizar esse meio, se certifique que sua chave tenha a <em>role</em> que gerencia as ações necessárias no Cloud SQL, portanto, dentre sua lista de permissões adicione <code>cloudsql.instance.connect</code> entre elas para que tenha disponível ações relacionados a:</p>
<ul>
<li>Cloud SQL Client</li>
<li>Cloud SQL Editor</li>
<li>Cloud SQL Admin</li>
</ul>
<h2 id="heading-criando-conexao-para-acesso-ao-banco-de-dados">Criando conexão para acesso ao banco de dados</h2>
<p>Abaixo temos um exemplo de execução do comando <code>cloud_sql_proxy</code> para criar uma nova conexão com uma instância, a partir do seu <em>Connection Name</em>, que é o mais recomendado porque ele atua como um “DNS” do recurso, sem recorrer a IP estáticos:</p>
<pre><code class="lang-bash">cloud_sql_proxy -instance=projecttest-1235:us-central1:my-cloudsql-instance=tcp:5432
</code></pre>
<p>Com o comando acima é disponibilizado um acesso a instância a partir do protocolo TCP, utilizando o host <em>localhost</em> e a porta <em>5432</em>. Quando é executado esse comando, aparece algumas informações interessantes que traz relação ao que falamos anteriormente:</p>
<pre><code><span class="hljs-attribute">2022</span>/<span class="hljs-number">03</span>/<span class="hljs-number">22</span> <span class="hljs-number">14</span>:<span class="hljs-number">56</span>:<span class="hljs-number">54</span> Rlimits for file descriptors set to {Current = <span class="hljs-number">8500</span>, Max = <span class="hljs-number">524288</span>}
<span class="hljs-attribute">2022</span>/<span class="hljs-number">03</span>/<span class="hljs-number">22</span> <span class="hljs-number">14</span>:<span class="hljs-number">56</span>:<span class="hljs-number">55</span> Listening <span class="hljs-literal">on</span> <span class="hljs-number">127.0.0.1:5432</span> for projecttest-<span class="hljs-number">1235</span>:us-central<span class="hljs-number">1</span>:my-cloudsql-instance
<span class="hljs-attribute">2022</span>/<span class="hljs-number">03</span>/<span class="hljs-number">22</span> <span class="hljs-number">14</span>:<span class="hljs-number">56</span>:<span class="hljs-number">55</span> Ready for new connections
<span class="hljs-attribute">2022</span>/<span class="hljs-number">03</span>/<span class="hljs-number">22</span> <span class="hljs-number">14</span>:<span class="hljs-number">56</span>:<span class="hljs-number">55</span> Generated RSA key in <span class="hljs-number">86</span>.<span class="hljs-number">817487</span>ms
</code></pre><p>Isso significa que a instância está disponível. Agora se tentarmos uma conexão ao banco de dados, utilizando o cliente da mesma ferramenta, que no nosso caso seria o <code>psql</code>, e passasse os dados necessários como o nome do banco, host, porta e credenciais, ele vai gerar o certificado efêmero e finalizar a comunicação para finalmente acessar a base:</p>
<pre><code class="lang-bash">psql -U postgres -h 0.0.0.0 -p 5432 -d my-database
</code></pre>
<pre><code><span class="hljs-attribute">2022</span>/<span class="hljs-number">03</span>/<span class="hljs-number">22</span> <span class="hljs-number">14</span>:<span class="hljs-number">57</span>:<span class="hljs-number">06</span> New connection for <span class="hljs-string">"projecttest-1235:us-central1:my-cloudsql-instance"</span>
<span class="hljs-attribute">2022</span>/<span class="hljs-number">03</span>/<span class="hljs-number">22</span> <span class="hljs-number">14</span>:<span class="hljs-number">57</span>:<span class="hljs-number">06</span> refreshing ephemeral certificate for instance projecttest-<span class="hljs-number">1235</span>:us-central<span class="hljs-number">1</span>:my-cloudsql-instance
<span class="hljs-attribute">2022</span>/<span class="hljs-number">03</span>/<span class="hljs-number">22</span> <span class="hljs-number">14</span>:<span class="hljs-number">57</span>:<span class="hljs-number">07</span> Scheduling refresh of ephemeral certificate in <span class="hljs-number">54</span>m<span class="hljs-number">59</span>.<span class="hljs-number">591751748</span>s
</code></pre>]]></content:encoded></item><item><title><![CDATA[Executando Cloud SQL proxy no docker-compose]]></title><description><![CDATA[Quando você tem alguma aplicação que precisa acessar uma base existente no Cloud SQL mas que a mesma se encontra em um container Docker, podemos fazer uso do docker-compose.yaml para definir os serviços.
Além da sua aplicação, vamos precisar de uma i...]]></description><link>https://blog.gilsondev.in/executando-cloud-sql-proxy-no-docker-compose</link><guid isPermaLink="true">https://blog.gilsondev.in/executando-cloud-sql-proxy-no-docker-compose</guid><category><![CDATA[cloud sql]]></category><category><![CDATA[Docker compose]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Tue, 20 Sep 2022 20:43:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1663710412270/NkJy1b_5x.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Quando você tem alguma aplicação que precisa acessar uma base existente no Cloud SQL mas que a mesma se encontra em um container Docker, podemos fazer uso do <code>docker-compose.yaml</code> para definir os serviços.</p>
<p>Além da sua aplicação, vamos precisar de uma imagem que possua o binário <code>cloud_sql_proxy</code>, como a imagem <code>gcr.io/cloudsql-docker/gce-proxy</code>.</p>
<p>Abaixo vamos fazer a execução e o <em>bind</em> via <em>adhoc</em> com o comando <code>docker</code> como exemplo:</p>
<pre><code class="lang-bash">docker run -d \
    -v PATH_TO_KEY_FILE:/config \
    -p 127.0.0.1:5432:5432 \
    gcr.io/cloudsql-docker/gce-proxy:1.32.0 /cloud_sql_proxy \
    -instances=INSTANCE_CONNECTION_NAME=tcp:0.0.0.0:5432 \
    -credential_file=/config
</code></pre>
<p>Seguindo essa mesma premissa, vamos preparar o <code>docker-compose.yaml</code></p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.8"</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-attr">app:</span>
    <span class="hljs-comment"># ...</span>

  <span class="hljs-attr">database:</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">"cloud-sql-proxy"</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">gcr.io/cloudsql-docker/gce-proxy:1.32.0</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./serviceAccount.json:/config</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">5432</span><span class="hljs-string">:5432</span>
    <span class="hljs-attr">command:</span> <span class="hljs-string">"/cloud_sql_proxy -instances=project-12e2:us-central1:database-instance=tcp:0.0.0.0:5432 -credential_file=/config"</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Mensageria de Alta Performance com Apache Kafka #2]]></title><description><![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.
Tópicos e Partições
O Kafka oferece binário...]]></description><link>https://blog.gilsondev.in/mensageria-de-alta-performance-com-apache-kafka-2</link><guid isPermaLink="true">https://blog.gilsondev.in/mensageria-de-alta-performance-com-apache-kafka-2</guid><category><![CDATA[kafka]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Mon, 24 Jun 2019 22:37:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1663708912536/VJto4teb8.jpg" length="0" type="image/jpeg"/><content:encoded><![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="heading-topicos-e-particoes">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="https://cdn.hashnode.com/res/hashnode/image/upload/v1663708884530/-GuxjbFnV.png" alt="image1.png" /></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="https://cdn.hashnode.com/res/hashnode/image/upload/v1663708894048/_6jjaT68y.png" alt="image2.png" /></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="heading-operacoes-nos-topicos">Operações nos Tópicos</h2>
<p>Por meio do <code>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>
<pre><code class="lang-shell">$ kafka-topics --zookeeper 127.0.0.1:2181 --create --topic first_topic --partition 3 --replication-factor 1
</code></pre>
<p>A sáida deve ser parecida com essa:</p>
<pre><code><span class="hljs-attribute">Created</span> topic <span class="hljs-string">"first_topic"</span>
</code></pre><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>
<pre><code class="lang-shell">$ kafka-topics --zookeeper 127.0.0.1:2181 --list
</code></pre>
<p>A saída deve ser parecida com essa:</p>
<pre><code><span class="hljs-attribute">__consumer_offsets</span>
logs-broker
first_topic
</code></pre><p><strong>Removendo tópicos</strong></p>
<pre><code class="lang-shell">$ kafka-topics --zookeeper 127.0.0.1:2181 --topic first_topic --delete
</code></pre>
<p>A saída deve ser parecida com essa:</p>
<pre><code>Topic first_topic <span class="hljs-keyword">is</span> marked <span class="hljs-keyword">for</span> deletion
Note: This will have <span class="hljs-keyword">no</span> impact <span class="hljs-keyword">if</span> <span class="hljs-keyword">delete</span>.topic.<span class="hljs-keyword">enable</span> <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">set</span> <span class="hljs-keyword">to</span> <span class="hljs-keyword">True</span>
</code></pre><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 target="_blank" 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>
<pre><code class="lang-shell">$ kafka-topics --zookeeper 127.0.0.1:2181 --topic first_topic --describe
</code></pre>
<p>A saída deve ser parecida com essa:</p>
<pre><code><span class="hljs-attribute">Topic</span>:first_topic       <span class="hljs-attribute">PartitionCount</span>:<span class="hljs-number">3</span>        <span class="hljs-attribute">ReplicationFactor</span>:<span class="hljs-number">1</span>     <span class="hljs-attribute">Configs</span>:
        <span class="hljs-attribute">Topic</span>: first_topic      <span class="hljs-attribute">Partition</span>: <span class="hljs-number">0</span>    <span class="hljs-attribute">Leader</span>: <span class="hljs-number">0</span>       <span class="hljs-attribute">Replicas</span>: <span class="hljs-number">0</span>     <span class="hljs-attribute">Isr</span>: <span class="hljs-number">0</span>
        <span class="hljs-attribute">Topic</span>: first_topic      <span class="hljs-attribute">Partition</span>: <span class="hljs-number">1</span>    <span class="hljs-attribute">Leader</span>: <span class="hljs-number">0</span>       <span class="hljs-attribute">Replicas</span>: <span class="hljs-number">0</span>     <span class="hljs-attribute">Isr</span>: <span class="hljs-number">0</span>
        <span class="hljs-attribute">Topic</span>: first_topic      <span class="hljs-attribute">Partition</span>: <span class="hljs-number">2</span>    <span class="hljs-attribute">Leader</span>: <span class="hljs-number">0</span>       <span class="hljs-attribute">Replicas</span>: <span class="hljs-number">0</span>     <span class="hljs-attribute">Isr</span>: <span class="hljs-number">0</span>
</code></pre><hr />
<h2 id="heading-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 target="_blank" href="http://kafka-console-producer.sh/"><code>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>
<pre><code class="lang-shell">$ kafka-console-producer.sh --broker-list localhost:9092 --topic first_topic
This is a message
This is another message
</code></pre>
<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  será uma mensagem enviada, então no exemplo acima foi enviado duas informações.</p>
<h2 id="heading-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 target="_blank" href="http://kafka-console-consumer.sh/"><code>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 _first<em>topic</em>:</p>
<pre><code>$ kafka<span class="hljs-operator">-</span>console<span class="hljs-operator">-</span>consumer.sh <span class="hljs-operator">-</span><span class="hljs-operator">-</span>bootstrap<span class="hljs-operator">-</span>server localhost:<span class="hljs-number">9092</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span>topic first_topic <span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-keyword">from</span><span class="hljs-operator">-</span>beginning
This <span class="hljs-keyword">is</span> a message
This <span class="hljs-keyword">is</span> another message
</code></pre><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>
<pre><code class="lang-shell">$ kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic first_topic --offset earliest
This is a message
This is another message
</code></pre>
<ul>
<li><strong>Consumindo a partir do offset inicial, de uma partição específica</strong></li>
</ul>
<pre><code class="lang-shell">$ kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic first_topic --partition 0 --offset earliest
This is a message
</code></pre>
<ul>
<li><strong>Consumindo a partir do offset final, de todas as partições</strong></li>
</ul>
<pre><code class="lang-shell">$ kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic first_topic --offset latest
Message sended after the two first
</code></pre>
<ul>
<li><strong>Consumindo a partir do offset final, de uma partição específica</strong></li>
</ul>
<pre><code class="lang-shell">$ kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic first_topic --partition 1 --offset latest
This is another message
Message sended after the two first
</code></pre>
<ul>
<li><strong>Consumindo a partir de um offset específico (índice 1)</strong></li>
</ul>
<pre><code class="lang-shell">$ kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic first_topic --offset 1
This is another message
</code></pre>
<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:encoded></item><item><title><![CDATA[Mensageria de Alta Performance com Apache Kafka #1]]></title><description><![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 v...]]></description><link>https://blog.gilsondev.in/mensageria-de-alta-performance-com-apache-kafka-1</link><guid isPermaLink="true">https://blog.gilsondev.in/mensageria-de-alta-performance-com-apache-kafka-1</guid><category><![CDATA[kafka]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Sat, 22 Jun 2019 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709088237/uKbIdm1Gp.jpg" length="0" type="image/jpeg"/><content:encoded><![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="heading-o-que-e-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="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709049385/EZS4O1S1I.png" alt="tipos_uso_kafka.png" /></p>
<p>Fonte: <a target="_blank" href="https://www.confluent.io/what-is-apache-kafka/">https://www.confluent.io/what-is-apache-kafka/</a></p>
<h3 id="heading-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="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709057301/Ixh7bPC81.png" alt="kafka_producer_consumer.png" /></p>
<p>Fonte: <a target="_blank" 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="heading-instalacao-e-configuracao">Instalação e Configuração</h3>
<p>O Kafka precisa do <a target="_blank" href="https://zookeeper.apache.org/">Apache Zookeeper</a> para coordenar os brokers do Kafka, principalmente quando envolve <a target="_blank" href="https://kafka.apache.org/documentation/#quickstart_multibroker">múltiplos brokers</a> em um ambiente clusterizado. Então siga as <a target="_blank" 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 target="_blank" 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>
<pre><code class="lang-shell">$ cd /opt
$ wget https://www-eu.apache.org/dist/kafka/2.2.1/kafka_2.11-2.2.1.tgz
$ tar -zxvf kafka_2.11-2.2.1.tgz
$ mv kafka_2.11-2.2.1 kafka
</code></pre>
<ul>
<li>Inicie o servidor Zookeeper caso não tenha feito, e após isso inicie o servidor do Kafka:</li>
</ul>
<pre><code class="lang-shell"># Iniciando Zookeeper

$ cd /opt/zookeeper
$ ./bin/zkServer.sh start

# Iniciando Kafka

$ cd /opt/kafka
$ ./bin/kafka-server-start.sh config/server.properties
</code></pre>
<p>Para confirmar se estão de pé, verifique se na sua máquina tem as seguintes portas abaixo:</p>
<pre><code class="lang-shell">$ netstat -tlpn | grep -E "2181|9092"
tcp6       0      0 :::9092                 :::*                    OUÇA       6531/java
tcp6       0      0 :::2181                 :::*                    OUÇA       6498/java
</code></pre>
<p>A porta 2181 é do Apache Zookeeper e o 9092 é a porta padrão do Apache Kafka.</p>
<h3 id="heading-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 target="_blank" 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 target="_blank" 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 target="_blank" 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 target="_blank" 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 target="_blank" href="https://cwiki.apache.org/confluence/display/KAFKA/Clients">outros clientes para várias linguagens</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709068809/Tvi3AfLZq.png" alt="kafka-apis.png" /></p>
<p>Fonte: <a target="_blank" 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:encoded></item><item><title><![CDATA[Usando o Airflow no Heroku]]></title><description><![CDATA[No post de hoje vamos mostrar como você pode usar o Airflow no famoso serviço Heroku, e como é simples fazer isso.
Preparando o ambiente
Antes de mais nada vamos deixar tudo pronto para podemos fazer isso, tanto no ambiente local como no Heroku. Prim...]]></description><link>https://blog.gilsondev.in/usando-o-airflow-no-heroku</link><guid isPermaLink="true">https://blog.gilsondev.in/usando-o-airflow-no-heroku</guid><category><![CDATA[airflow]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Mon, 24 Dec 2018 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709247497/ogfdbqBzW.png" length="0" type="image/jpeg"/><content:encoded><![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="heading-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>
<pre><code class="lang-shell">$ mkdir airflow-heroku &amp;&amp; cd airflow-heroku
$ python3.6 -m venv .venv
</code></pre>
<p>Ative o ambiente e instale as dependências. Não esqueça de salvar no arquivo <code>requirements.txt</code> para que o Heroku saiba o que instalar:</p>
<pre><code class="lang-shell">$ source .venv/bin/activate
$ pip install "apache-airflow[postgres,password]" cryptography
$ pip freeze &gt; requirements.txt
</code></pre>
<p>Agora precisamos usar o <code>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>airflow.cfg</code>:</p>
<pre><code class="lang-shell">$ export AIRFLOW_HOME=$PWD  # Definindo a raiz do airflow na pasta corrente
$ airflow initdb
</code></pre>
<p>O airflow irá criar vários arquivos e pastas como o exemplo abaixo:</p>
<pre><code class="lang-shell">.
├── airflow.cfg
├── airflow.db
├── logs
└── unittests.cfg
</code></pre>
<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>
<pre><code class="lang-shell">$ git init
$ cat &lt;&lt;EOF &gt;&gt; .gitignore
*.pyc
*.db
.venv/
logs/
EOF
</code></pre>
<p><strong>Observação</strong>: Ao executar com Python 3.7, pode ocorrer de aparecer a seguinte saída:</p>
<pre><code>ERROR [airflow.models.DagBag] Failed to <span class="hljs-keyword">import</span>: <span class="hljs-operator">/</span><span class="hljs-title">tmp</span><span class="hljs-operator">/</span><span class="hljs-title">airflow</span><span class="hljs-operator">-</span><span class="hljs-title">heroku</span><span class="hljs-operator">/</span>.<span class="hljs-title">venv</span><span class="hljs-operator">/</span><span class="hljs-title">lib</span><span class="hljs-operator">/</span><span class="hljs-title">python3</span>.7<span class="hljs-operator">/</span><span class="hljs-title">site</span><span class="hljs-operator">-</span><span class="hljs-title">packages</span><span class="hljs-operator">/</span><span class="hljs-title">airflow</span><span class="hljs-operator">/</span><span class="hljs-title">example_dags</span><span class="hljs-operator">/</span><span class="hljs-title">example_http_operator</span>.<span class="hljs-title">py</span>
<span class="hljs-title">Traceback</span> (<span class="hljs-title">most</span> <span class="hljs-title">recent</span> <span class="hljs-title">call</span> <span class="hljs-title">last</span>):
  <span class="hljs-title">File</span> <span class="hljs-string">"/tmp/airflow-heroku/.venv/lib/python3.7/site-packages/airflow/models.py"</span>, <span class="hljs-title">line</span> 377, <span class="hljs-title">in</span> <span class="hljs-title">process_file</span>
    <span class="hljs-title">m</span> <span class="hljs-operator">=</span> <span class="hljs-title">imp</span>.<span class="hljs-title">load_source</span>(<span class="hljs-title">mod_name</span>, <span class="hljs-title">filepath</span>)
  <span class="hljs-title">File</span> <span class="hljs-string">"/home/gilson/.pyenv/versions/3.7.1/lib/python3.7/imp.py"</span>, <span class="hljs-title">line</span> 171, <span class="hljs-title">in</span> <span class="hljs-title">load_source</span>
    <span class="hljs-title">module</span> <span class="hljs-operator">=</span> <span class="hljs-title">_load</span>(<span class="hljs-title">spec</span>)
  <span class="hljs-title">File</span> <span class="hljs-string">"&lt;frozen importlib._bootstrap&gt;"</span>, <span class="hljs-title">line</span> 696, <span class="hljs-title">in</span> <span class="hljs-title">_load</span>
  <span class="hljs-title">File</span> <span class="hljs-string">"&lt;frozen importlib._bootstrap&gt;"</span>, <span class="hljs-title">line</span> 677, <span class="hljs-title">in</span> <span class="hljs-title">_load_unlocked</span>
  <span class="hljs-title">File</span> <span class="hljs-string">"&lt;frozen importlib._bootstrap_external&gt;"</span>, <span class="hljs-title">line</span> 728, <span class="hljs-title">in</span> <span class="hljs-title">exec_module</span>
  <span class="hljs-title">File</span> <span class="hljs-string">"&lt;frozen importlib._bootstrap&gt;"</span>, <span class="hljs-title">line</span> 219, <span class="hljs-title">in</span> <span class="hljs-title">_call_with_frames_removed</span>
  <span class="hljs-title">File</span> <span class="hljs-string">"/tmp/airflow-heroku/.venv/lib/python3.7/site-packages/airflow/example_dags/example_http_operator.py"</span>, <span class="hljs-title">line</span> 27, <span class="hljs-title">in</span> <span class="hljs-operator">&lt;</span><span class="hljs-title">module</span><span class="hljs-operator">&gt;</span>
    <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">airflow</span>.<span class="hljs-title">operators</span>.<span class="hljs-title">http_operator</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">SimpleHttpOperator</span>
  <span class="hljs-title">File</span> <span class="hljs-string">"/tmp/airflow-heroku/.venv/lib/python3.7/site-packages/airflow/operators/http_operator.py"</span>, <span class="hljs-title">line</span> 21, <span class="hljs-title">in</span> <span class="hljs-operator">&lt;</span><span class="hljs-title">module</span><span class="hljs-operator">&gt;</span>
    <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">airflow</span>.<span class="hljs-title">hooks</span>.<span class="hljs-title">http_hook</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">HttpHook</span>
  <span class="hljs-title">File</span> <span class="hljs-string">"/tmp/airflow-heroku/.venv/lib/python3.7/site-packages/airflow/hooks/http_hook.py"</span>, <span class="hljs-title">line</span> 23, <span class="hljs-title">in</span> <span class="hljs-operator">&lt;</span><span class="hljs-title">module</span><span class="hljs-operator">&gt;</span>
    <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">tenacity</span>
  <span class="hljs-title">File</span> <span class="hljs-string">"/tmp/airflow-heroku/.venv/lib/python3.7/site-packages/tenacity/__init__.py"</span>, <span class="hljs-title">line</span> 352
    <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-title">tenacity</span>.<span class="hljs-title">async</span> <span class="hljs-title"><span class="hljs-keyword">import</span></span> <span class="hljs-title">AsyncRetrying</span>
                      <span class="hljs-operator">^</span>
<span class="hljs-title">SyntaxError</span>: <span class="hljs-title">invalid</span> <span class="hljs-title">syntax</span>
</code></pre><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>generate_fernet_key.py</code> e insira na pasta <code>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>
<pre><code class="lang-python"><span class="hljs-comment">#!/usr/bin/env python</span>

<span class="hljs-keyword">from</span> cryptography <span class="hljs-keyword">import</span> fernet

print(fernet.Fernet.generate_key().decode(<span class="hljs-string">"utf-8"</span>))
</code></pre>
<p>Dessa forma que preparamos o ambiente, já podemos criar as DAGS e fazer o teste rodando localmente.</p>
<h2 id="heading-tornando-o-ambiente-mais-semelhante-ao-de-producao">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>airflow.cfg</code></p>
<p>O arquivo <code>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>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 target="_blank" 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>
<pre><code class="lang-config"># ...
sql_alchemy_conn = postgresql+psycopg2://postgres:postgres@localhost:5432/airflow

# ...
executor = LocalExecutor

# ...
fernet_key = $(python "contrib/generate_fernet_key.py")
</code></pre>
<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>airflow.db</code> e rode novamente o <code>airflow initdb</code> para que as migrations do airflow seja aplicados no banco.</p>
<h2 id="heading-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 target="_blank" 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>config:set</code>. Lembre-se de criar o app e os addons necessários:</p>
<pre><code class="lang-shell">$ heroku apps:create heroku-airflow
$ heroku addons:create heroku-postgresql:hobby-dev
</code></pre>
<pre><code class="lang-shell">$ heroku config:set SLUGIFY_USES_TEXT_UNIDECODE=yes
$ heroku config:set AIRFLOW_HOME="/app"
$ heroku config:set AIRFLOW__CORE__DAGS_FOLDER="/app/dags"
$ heroku config:set AIRFLOW__CORE__FERNET_KEY=`python contrib/generate_fernet_key.py`
$ heroku config:set AIRFLOW__CORE__LOAD_EXAMPLES=False
$ heroku config:set AIRFLOW__CORE__SQL_ALCHEMY_CONN=`heroku config:get DATABASE_URL`
$ heroku config:set AIRFLOW__CORE__EXECUTOR="LocalExecutor"
</code></pre>
<p>Crie o arquivo <code>Procfile</code> para o Heroku inicializar a aplicação corretamente:</p>
<pre><code class="lang-shell">release: airflow initdb
web: airflow webserver -p $PORT --daemon &amp;&amp; airflow scheduler
</code></pre>
<p>Agora vamos versionar e enviar os arquivos para o Heroku:</p>
<pre><code class="lang-shell">$ git add .
$ git commit -m "Arquivos para o ambiente Airflow"
$ git push heroku master
</code></pre>
<p>Com isso conseguimos fazer com que o Airflow execute no ambiente:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709199709/aB_g4if-A.png" alt="airflow_heroku_worked_1.png" /></p>
<p>Mas como pode perceber, deixar aberto o painel dessa forma não é seguro. Vamos tornar mais seguro.</p>
<h3 id="heading-inserindo-autenticacao-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>
<pre><code class="lang-shell">heroku config:set AIRFLOW__WEBSERVER__AUTHENTICATE=True
heroku config:set AIRFLOW__WEBSERVER__AUTH_BACKEND="airflow.contrib.auth.backends.password_auth"
</code></pre>
<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="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709205918/HetrSTKGu.png" alt="airflow_heroku_worked_2.png" /></p>
<p>Agora que está preparado para autenticar, vamos criar um novo usuário. Acesse o shell do Python no Heroku com <code>heroku run python</code> e execute os comandos abaixo:</p>
<pre><code class="lang-shell">import airflow
from airflow import models, settings
from airflow.contrib.auth.backends.password_auth import PasswordUser

user = PasswordUser(models.User())
user.username = 'admin'
user.email = 'me@gilsondev.in'
user.password = 'password'

session = settings.Session()
session.add(user)
session.commit()
session.close()
exit()
</code></pre>
<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="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709216262/S7tte1G4v.png" alt="airflow_heroku_worked_1.png" /></p>
<p>E pronto! Temos um airflow preparado no Heroku para receber suas DAGs.</p>
<h2 id="heading-bonus-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>dags</code> na raiz do projeto e crie dentro dele um módulo chamado <code>tutorial_dag.py</code>:</p>
<pre><code class="lang-shell">"""
Code that goes along with the Airflow tutorial located at:
https://github.com/apache/incubator-airflow/blob/master/airflow/example_dags/tutorial.py
"""
from airflow import DAG
from airflow.operators.bash_operator import BashOperator
from datetime import datetime, timedelta


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

dag = DAG('tutorial', default_args=default_args, schedule_interval=timedelta(days=1))

# t1, t2 and t3 are examples of tasks created by instantiating operators
t1 = BashOperator(
    task_id='print_date',
    bash_command='date',
    dag=dag)

t2 = BashOperator(
    task_id='sleep',
    bash_command='sleep 5',
    retries=3,
    dag=dag)

templated_command = """
    {% for i in range(5) %}
        echo "{{ ds }}"
        echo "{{ macros.ds_add(ds, 7)}}"
        echo "{{ params.my_param }}"
    {% endfor %}
"""

t3 = BashOperator(
    task_id='templated',
    bash_command=templated_command,
    params={'my_param': 'Parameter I passed in'},
    dag=dag)

t2.set_upstream(t1)
t3.set_upstream(t1)
</code></pre>
<p>Vamos aproveitar o código que está no <a target="_blank" 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>
<pre><code class="lang-shell">$ git add .
$ git commit -m "Inserção da primeira DAG"
$ git push heroku master
</code></pre>
<p>Agora acesse novamente o seu Airflow que você verá a DAG que acabamos de criar:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709228929/Ir7-5Ygg1.png" alt="airflow_heroku_worked_3.png" /></p>
<p>E agora ativando o schedule da DAG, já começou sua execução:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709235056/iSbd0Dfmf.png" alt="airflow_heroku_worked_4.png" /></p>
<p>Depois de vários passos, agora temos um Airflow preparado para usar no Heroku.</p>
<p>Até mais pessoal!</p>
]]></content:encoded></item><item><title><![CDATA[Criando testes de unmanaged models no Django]]></title><description><![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...]]></description><link>https://blog.gilsondev.in/criando-testes-de-unmanaged-models-no-django</link><guid isPermaLink="true">https://blog.gilsondev.in/criando-testes-de-unmanaged-models-no-django</guid><category><![CDATA[Django]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Sun, 02 Dec 2018 22:37:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/g5_rxRjvKmg/upload/v1663710177801/tHNtFTlHJ3.jpeg" length="0" type="image/jpeg"/><content:encoded><![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 target="_blank" 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="heading-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>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Question</span>(<span class="hljs-params">models.Model</span>):</span>
    text = models.TextField()
</code></pre>
<p>Segue um modelo bem simples, mas que ele diz muita coisa para a ORM poder criar uma tabela como definida abaixo:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> app_question(
    <span class="hljs-keyword">id</span> <span class="hljs-built_in">int</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">null</span>,
    <span class="hljs-built_in">text</span> <span class="hljs-built_in">text</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">null</span>,
    PRIMARY <span class="hljs-keyword">KEY</span>(<span class="hljs-keyword">id</span>)
);
</code></pre>
<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 target="_blank" 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="heading-classes-nao-gerenciaveis-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 target="_blank" 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="heading-uma-abordagem-simples">Uma abordagem simples</h2>
<p>Buscando alternativas de lidar com isso encontrei uma solução que é <a target="_blank" href="https://kdazzle.svbtle.com/testing-with-unmanaged-databases-in-django">alterar o valor do managed no teste</a> ou <a target="_blank" 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>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LegacyTable</span>(<span class="hljs-params">models.Model</span>):</span>
    id = models.PositiveIntegerField(primary_key=<span class="hljs-literal">True</span>, db_column=<span class="hljs-string">"cod_legacy"</span>)
    name = models.CharField(max_length=<span class="hljs-number">255</span>, db_column=<span class="hljs-string">"txt_name"</span>)

    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Meta</span>:</span>
        db_table = <span class="hljs-string">"legacy_table"</span>
        managed = <span class="hljs-literal">False</span>
</code></pre>
<p>Criando ele e rodar o <code>makemigrations</code> para o app que o modelo está, irá criar uma migração parecido com essa:</p>
<pre><code class="lang-python"><span class="hljs-comment"># -*- coding: utf-8 -*-</span>
<span class="hljs-keyword">from</span> __future__ <span class="hljs-keyword">import</span> unicode_literals

<span class="hljs-keyword">from</span> django.db <span class="hljs-keyword">import</span> migrations, models


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Migration</span>(<span class="hljs-params">migrations.Migration</span>):</span>

    dependencies = []

    operations = [
        migrations.CreateModel(
            name=<span class="hljs-string">'LegacyTable'</span>,
            fields=[
                (<span class="hljs-string">'id'</span>, models.PositiveIntegerField(db_column=<span class="hljs-string">'cod_legacy'</span>, primary_key=<span class="hljs-literal">True</span>, serialize=<span class="hljs-literal">False</span>)),
                (<span class="hljs-string">'name'</span>, models.CharField(db_column=<span class="hljs-string">'txt_name'</span>, max_length=<span class="hljs-number">255</span>)),
            ],
            options={
                <span class="hljs-string">'db_table'</span>: <span class="hljs-string">'legacy_table'</span>,
                <span class="hljs-string">'managed'</span>: <span class="hljs-literal">False</span>,
            },
        ),
    ]
</code></pre>
<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>
<pre><code class="lang-python"><span class="hljs-comment"># settings.py</span>

<span class="hljs-comment"># ...</span>
DEBUG = <span class="hljs-literal">True</span>
TESTING = <span class="hljs-literal">True</span>
</code></pre>
<p>E com isso alteramos a migração para ficar assim:</p>
<pre><code class="lang-python"><span class="hljs-comment"># -*- coding: utf-8 -*-</span>
<span class="hljs-keyword">from</span> __future__ <span class="hljs-keyword">import</span> unicode_literals

<span class="hljs-keyword">from</span> django.db <span class="hljs-keyword">import</span> migrations, models
<span class="hljs-keyword">from</span> django.conf <span class="hljs-keyword">import</span> settings


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Migration</span>(<span class="hljs-params">migrations.Migration</span>):</span>

    dependencies = []

    operations = [
        migrations.CreateModel(
            name=<span class="hljs-string">'LegacyTable'</span>,
            fields=[
                (<span class="hljs-string">'id'</span>, models.PositiveIntegerField(db_column=<span class="hljs-string">'cod_legacy'</span>, primary_key=<span class="hljs-literal">True</span>, serialize=<span class="hljs-literal">False</span>)),
                (<span class="hljs-string">'name'</span>, models.CharField(db_column=<span class="hljs-string">'txt_name'</span>, max_length=<span class="hljs-number">255</span>)),
            ],
            options={
                <span class="hljs-string">'db_table'</span>: <span class="hljs-string">'legacy_table'</span>,
                <span class="hljs-string">'managed'</span>: settings.TESTING,
            },
        ),
    ]
</code></pre>
<p>Dessa forma, quando rodarmos <code>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>TESTING</code> precisa estar <code>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:encoded></item><item><title><![CDATA[Automatizando seu fluxo de trabalho com Airflow]]></title><description><![CDATA[É comum surgir situações em que precisamos fazer alguns procedimentos repetitivos em diversos projetos, principalmente quando envolve dados.
Um exemplo disso:

Migrar dados de uma tabela para outra
Importar dados a partir de várias fontes e juntar em...]]></description><link>https://blog.gilsondev.in/automatizando-seu-fluxo-de-trabalho-com-airflow</link><guid isPermaLink="true">https://blog.gilsondev.in/automatizando-seu-fluxo-de-trabalho-com-airflow</guid><category><![CDATA[airflow]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Sat, 23 Jun 2018 22:37:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/KyreMg96fuA/upload/v1663710341888/pHeTbRxIO.jpeg" length="0" type="image/jpeg"/><content:encoded><![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="heading-o-que-e-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 target="_blank" 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 target="_blank" href="https://github.com/apache/incubator-airflow">apache-airflow</a>.</p>
<h2 id="heading-instalacao-e-configuracao-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>
<pre><code class="lang-shell">$ mkdir airflow-tutorial
$ cd airflow-tutorial
$ python3 -m venv .venv
$ source .venv/bin/activate
</code></pre>
<p>Instale o airflow usando <em>pip</em>:</p>
<pre><code class="lang-shell">(.venv)$ pip install apache-airflow
</code></pre>
<p>Conforme a <a target="_blank" 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>
<pre><code class="lang-shell"># Definindo o diretório airflow-tutorial como raiz do Airflow
$ export AIRFLOW_HOME=$PWD
</code></pre>
<p>Agora vamos gerar o ambiente com o comando abaixo.</p>
<pre><code class="lang-shell">(.venv)$ airflow initdb
</code></pre>
<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>dags</code>, em que vamos armazenar os fluxos implementados. Mais na frente irei explicar melhor:</p>
<pre><code class="lang-shell">(.venv)$ mkdir dags
</code></pre>
<p>Após preparar tudo, vamos iniciar o servidor local para vermos o painel administrativo da ferramenta.</p>
<pre><code class="lang-shell">(.venv)$ airflow webserver
</code></pre>
<p>Agora é acessar o <a target="_blank" href="http://localhost:8080">http://localhost:8080.</a> Feito isso você vai ver algo parecido com isso:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709870717/m3IliiFlo.png" alt="airflow_home.png" /></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="heading-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://cdn.hashnode.com/res/hashnode/image/upload/v1663709880332/BN89kF9qR.png" alt="fluxo-tutorial.png" /></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="heading-explicando-a-estrutura-de-uma-dag">Explicando a estrutura de uma DAG</h2>
<p>Essa estrutura que preparamos, para o Airflow é chamada de <a target="_blank" 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>dags</code> que criamos anteriormente. Você <a target="_blank" 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="heading-vamos-ao-codigo">Vamos ao código</h2>
<p>Explicado como é um workflow para o Airflor, vamos criar a DAG do nosso exemplo.</p>
<h3 id="heading-preparando-a-dag">Preparando a DAG</h3>
<p>Crie um novo arquivo chamado de <code>fluxo_simples.py</code> na pasta <code>dags</code> e implemente o código abaixo:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> airflow

<span class="hljs-keyword">from</span> airflow <span class="hljs-keyword">import</span> DAG
<span class="hljs-keyword">from</span> airflow.operators.bash_operator <span class="hljs-keyword">import</span> BashOperator
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span>, timedelta

args = {
    <span class="hljs-string">'owner'</span>: <span class="hljs-string">'airflow'</span>,
    <span class="hljs-string">'start_date'</span>: airflow.utils.dates.days_ago(<span class="hljs-number">2</span>)
}

dag = DAG(
    dag_id=<span class="hljs-string">'fluxo_simples'</span>,
    default_args=args,
    schedule_interval=timedelta(days=<span class="hljs-number">1</span>),
    dagrun_timeout=timedelta(minutes=<span class="hljs-number">60</span>)
)
</code></pre>
<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 _fluxo<em>simples</em>: <code>dag_id</code></li>
<li>Recebe argumentos padrão e que reflete nos nós do grafo: <code>defaul_args</code></li>
<li>Define a periodicidade de execução. Nesse caso é diária: <code>schedule_interval</code></li>
<li>Define o tempo em que a DAG terá antes de ser interrompida: <code>dagrun_timeout</code></li>
</ul>
<p>A variável <code>dag</code> será necessária para vincular os nós que vamos criar.</p>
<h3 id="heading-criando-os-nos-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>
<pre><code class="lang-python"><span class="hljs-comment"># (...)</span>
<span class="hljs-comment"># Depois da preparação da DAG</span>

<span class="hljs-comment"># 1. Imprime a data na saída padrão</span>
task1 = BashOperator(
    task_id=<span class="hljs-string">'print_date'</span>,
    bash_command=<span class="hljs-string">'date'</span>,
    dag=dag)

<span class="hljs-comment"># 2. Faz uma sleep de 5 segundos.</span>
<span class="hljs-comment"># Dando errado tente em no máximo 3 vezes</span>
task2 = BashOperator(
    task_id=<span class="hljs-string">'sleep'</span>,
    bash_command=<span class="hljs-string">'sleep 5'</span>,
    retries=<span class="hljs-number">3</span>,
    dag=dag)

<span class="hljs-comment"># 3. Salve a data em um arquivo texto</span>
task3 = BashOperator(
    task_id=<span class="hljs-string">'save_date'</span>,
    bash_command=<span class="hljs-string">'date &gt; /tmp/date_output.txt'</span>,
    retries=<span class="hljs-number">3</span>,
    dag=dag)
</code></pre>
<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>Operators</code>, que são classes Python que oferece variados tipos de processamento. Aqui estamos usando do tipo <code>BashOperator</code> em que iremos executar comandos bash no console, passados no argumento <code>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>dag</code>.</p>
<p>Por fim, precisamos definir a interligação dos nós e sua ordem de execução:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Interligando task1 ao task2</span>
task1.set_downstream(task2)

<span class="hljs-comment"># Interligando task2 ao task3</span>
task2.set_downstream(task3)
</code></pre>
<p>Cada tarefa instanciada possui métodos para fazer ligação entre eles. No exemplo acima, usamos o <code>set_downstream</code> para definir que será a próxima tarefa a interligar no nó em questão. Temos também o <code>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://cdn.hashnode.com/res/hashnode/image/upload/v1663709892807/oDyECnkYU.png" alt="fluxo-tutorial-2.png" /></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 target="_blank" href="https://airflow.apache.org/code.html#operators">vários operators</a> disponíveis, como existem outros <a target="_blank" href="https://github.com/airflow-plugins">operators</a> criados e mantidos pela comunidade.</p>
<h3 id="heading-agora-e-executar">Agora é executar</h3>
<p>Com a nossa DAG totalmente finalizada, vamos testar executando na linha de comando:</p>
<pre><code class="lang-shell">(.venv)$ airflow backfill fluxo_simples -s 2018-06-09
</code></pre>
<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>
<pre><code class="lang-shell">[2018-06-09 12:14:36,996] {base_task_runner.py:98} INFO - Subtask: [2018-06-09 12:14:36,996] {bash_operator.py:97} INFO - Output:
[2018-06-09 12:14:36,997] {base_task_runner.py:98} INFO - Subtask: [2018-06-09 12:14:36,997] {bash_operator.py:101} INFO - Sáb Jun 09 12:14:36 -03 2018
</code></pre>
<p>Aqui ele executa o comando <code>date</code> e traz o resultado para a saída padrão.</p>
<pre><code class="lang-shell">[2018-06-09 12:14:53,475] {base_task_runner.py:98} INFO - Subtask: [2018-06-09 12:14:53,475] {bash_operator.py:88} INFO - Running command: date &gt; /tmp/date_output.txt
[2018-06-09 12:14:53,477] {base_task_runner.py:98} INFO - Subtask: [2018-06-09 12:14:53,477] {bash_operator.py:97} INFO - Output:
[2018-06-09 12:14:53,479] {base_task_runner.py:98} INFO - Subtask: [2018-06-09 12:14:53,479] {bash_operator.py:105} INFO - Command exited with return code 0
</code></pre>
<p>E por aqui mostra que a execução do <code>date</code> e o resultado salvo em um arquivo foi feito com sucesso. Se for fazer um <code>cat</code> no arquivo gerado vai encontrar algo parecido:</p>
<pre><code class="lang-shell">(.venv)$ cat /tmp/date_output.txt
Sáb Jun 09 12:14:53 -03 2018
</code></pre>
<h2 id="heading-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 target="_blank" href="https://airflow.apache.org/scheduler.html">preparar o agendamento</a>, caso isso esteja habilitado:</p>
<pre><code class="lang-shell">$ airflow trigger_dag fluxo_simples -e 2018-06-10
</code></pre>
<p>Testar uma tarefa específica, ao invés de toda a DAG:</p>
<pre><code class="lang-shell">$ airflow test fluxo_simples print_date 2018-06-09
</code></pre>
<p>Nesse caso ele se basea em alguma configuração específica para testes no arquivo <code>unittest.cfg</code> e que não precisa usar alguma configuração no <code>airflow.cfg</code>.</p>
<p>Caso queira saber o status da execução de uma DAG:</p>
<pre><code class="lang-shell">$ airflow dag_state fluxo_simples 2018-06-09
</code></pre>
<p>Executar uma tarefa específica, ao invés de toda a DAG:</p>
<pre><code class="lang-shell">$ airflow run fluxo_simples print_date 2018-06-09
</code></pre>
<p>Listar todas as tarefas de uma DAG:</p>
<pre><code class="lang-shell">$ airflow list_tasks fluxo_simples
</code></pre>
<p>Listar todas as DAGS disponíveis:</p>
<pre><code class="lang-shell">$ airflow list_dags
</code></pre>
<p>Iniciar uma instância de scheduler. Isso vamos aprofundar quando abordarmos sobre os <code>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>
<pre><code class="lang-shell">$ airflow scheduler
</code></pre>
<p>Limpar o conjunto de instâncias de tarefas de uma DAG, como se nunca tivesse sido executado:</p>
<pre><code class="lang-shell">$ airflow clear fluxo_simples
</code></pre>
<h2 id="heading-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>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://cdn.hashnode.com/res/hashnode/image/upload/v1663709908315/Ip8MW_wd0.png" alt="painel1.png" /></p>
<ol>
<li>Podemos ver abaixo a linha com a nossa DAG. Quando executei estava com o nome <code>tutorial</code>, mas não é nada diferente do que fizemos nesse post:</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709913300/2eu6UJXbe.png" alt="painel2.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709920295/x6HHvEg16.png" alt="painel3.png" /></p>
<p>Explicando cada tipo de estado e a quantidade em que cada DAG e tarefa o Airflow registrou:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709927676/tytXa4Dq1.png" alt="detalhes-painel1.png" /></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://cdn.hashnode.com/res/hashnode/image/upload/v1663709940442/isL3ZrLVG.png" alt="painel-treeview.png" /></p>
<p><strong>Visão em grafo</strong>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709950156/TUziPEYvB.png" alt="painel-graphview.png" /></p>
<p><strong>Visão por tarefa e sua duração</strong>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709958093/4CWfTXDwT.png" alt="painel-taskduration.png" /></p>
<p><strong>Visão em Gantt da execução das tarefas</strong>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709966163/HlYLe9HKu.png" alt="painel-gantt.png" /></p>
<p><strong>Visão do código da DAG</strong>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1663709973609/kGBejmePo.png" alt="painel-codeview.png" /></p>
<h2 id="heading-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.</p>
<p>Espero que tenham gostado. Quaisquer dúvidas e sugestões comentem embaixo e vamos trocar idéias.</p>
<p>Até mais!</p>
]]></content:encoded></item><item><title><![CDATA[Visualizando migrations dependentes com django-migrations-graph]]></title><description><![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...]]></description><link>https://blog.gilsondev.in/visualizando-migrations-dependentes-com-django-migrations-graph</link><guid isPermaLink="true">https://blog.gilsondev.in/visualizando-migrations-dependentes-com-django-migrations-graph</guid><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Mon, 22 May 2017 22:37:25 GMT</pubDate><content:encoded><![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 target="_blank" 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="heading-instalacao">Instalação</h2>
<p>Antes de mais nada, vamos preparar um ambiente virtualenv e criar um projeto de exemplo:</p>
<pre><code class="lang-shell">$ python3 -m venv .venv
$ pip install django
$ django-admin startproject graph_example .
</code></pre>
<p>Depois, vamos instalar o pacote e inserir o aplicativo no INSTALLED_APPS do projeto:</p>
<pre><code class="lang-shell">$ pip install django-migrations-graph
</code></pre>
<pre><code class="lang-python">INSTALLED_APPS = [
    <span class="hljs-string">'django.contrib.admin'</span>,
    <span class="hljs-string">'django.contrib.auth'</span>,
    <span class="hljs-string">'django.contrib.contenttypes'</span>,
    <span class="hljs-string">'django.contrib.sessions'</span>,
    <span class="hljs-string">'django.contrib.messages'</span>,
    <span class="hljs-string">'django.contrib.staticfiles'</span>,

    <span class="hljs-comment"># Third Apps</span>
    <span class="hljs-string">'migraph'</span>,
]
</code></pre>
<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>
<pre><code class="lang-shell">$ python manage.py migration_dependencies auth admin
[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

[admin]
admin/0001_initial
        Depends on:
                auth/__first__
                contenttypes/__first__
admin/0002_logentry_remove_auto_add
</code></pre>
<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:encoded></item><item><title><![CDATA[Python decouple e Travis CI]]></title><description><![CDATA[Para quem trabalha com projetos em Python e Django, talvez deva conhecer a iniciativa do Henrique Bastos que é o projeto python-decouple, que tem o intuito de facilitar a separação de dados de configuração com o código da sua aplicação. Antes era est...]]></description><link>https://blog.gilsondev.in/python-decouple-e-travis-ci</link><guid isPermaLink="true">https://blog.gilsondev.in/python-decouple-e-travis-ci</guid><category><![CDATA[Python]]></category><category><![CDATA[TravisCI]]></category><dc:creator><![CDATA[Gilson Filho]]></dc:creator><pubDate>Mon, 15 May 2017 22:37:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1663710068290/rEbxWGBem.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Para quem trabalha com projetos em Python e Django, talvez deva conhecer a iniciativa do Henrique Bastos que é o projeto <a target="_blank" href="https://github.com/henriquebastos/python-decouple">python-decouple</a>, que tem o intuito de facilitar a separação de dados de configuração com o código da sua aplicação. Antes era estrito para o Django, mas com o tempo ele tornou flexível para quem usa outros frameworks Python que forem usar. Esse procedimento de separação é uma das dicas recomendadas pelo <a target="_blank" href="https://12factor.net/pt_br/">Twelve-Factors App</a>, que é armazenar as configurações no ambiente em que o projeto será rodado.</p>
<p>Nesse post vamos supor que temos um pequeno projeto em Django, e precisamos definir um valor de configuração para o arquivo settings.py. Para isso vamos importar a função config do pacote para procurar o valor em uma variável nos arquivos que ele busca.</p>
<p>Ele basicamente passa a ler informações de configuração em arquivos .env ou com a extensão .ini. Caso a variável definida não encontrar, ele usa um valor padrão, se definido. Abaixo segue um pequeno exemplo pego no README do projeto:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> decouple <span class="hljs-keyword">import</span> config
</code></pre>
<p>Usamos a função para recuperar os valores:</p>
<pre><code class="lang-python">SECRET_KEY = config(<span class="hljs-string">'SECRET_KEY'</span>)
DEBUG = config(<span class="hljs-string">'DEBUG'</span>, default=<span class="hljs-literal">False</span>, cast=bool)
EMAIL_HOST = config(<span class="hljs-string">'EMAIL_HOST'</span>, default=<span class="hljs-string">'localhost'</span>)
EMAIL_PORT = config(<span class="hljs-string">'EMAIL_PORT'</span>, default=<span class="hljs-number">25</span>, cast=int)
</code></pre>
<p>Assim, se no arquivo .env ou um .ini tenha uma variável <strong>SECRET_KEY</strong>, então ele irá atribuir o valor ao existente no arquivo de configuração da aplicação. Para mais detalhes da aplicação, veja o README do projeto.</p>
<h2 id="heading-usando-integracao-continua">Usando integração contínua</h2>
<p>Para quem usa serviços de integração contínua, é necessário colocar configurações da aplicação para que possa executar o trabalho desejado, que normalmente é executar os testes automatizados para caso todos passarem, efetuar uma segunda ação (enviar cobertura, efetuar deploy em um PaaS, etc). Aqui, com o Travis CI, caso queiramos definir valores de configuração, a boa prática é não colocar no código, mas em variáveis de ambiente. No caso do Travis CI, podemos fazer da seguinte forma:</p>
<pre><code class="lang-python">language: python
 python:
   - <span class="hljs-string">"3.5"</span>
 <span class="hljs-keyword">global</span>:
   -DEBUG=<span class="hljs-literal">False</span>
   -SECRET_KEY=<span class="hljs-string">"vg$ita%40wm=q1h4sr2jf+zjzyi*dt!*j-@8(b0b(4#d(0#a67"</span>
 install:
   <span class="hljs-comment"># (...)</span>
</code></pre>
<p>Assim, no arquivo de configuração do seu projeto, você usa o os.environ.get do módulo os do Python para capturar o valor, como o exemplo abaixo:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os

<span class="hljs-comment"># (...)</span>

SECRET_KEY = os.environ.get(<span class="hljs-string">'SECRET_KEY'</span>, <span class="hljs-string">''</span>)
</code></pre>
<p>Dessa forma, ao executar o job, as variáveis serão inseridas no ambiente do Travis, e então quando a aplicação ler seu arquivo de configuração, o SECRET_KEY irá possuir o valor desejado, porque o módulo pegou da variável de ambiente. Mas no caso do python-decouple, ele não lê dessa forma, mas como a documentação orienta, nos arquivos ditos anteriormente. Como fazer com essa situação?</p>
<h2 id="heading-adaptando-o-job">Adaptando o job</h2>
<p>A solução que encontrei, foi usar a opção <code>before_script</code> do .travis.yml para fazer uma escrita de um arquivo .env usando o comando <code>echo</code>:</p>
<pre><code class="lang-python">language: python
 python:
   - <span class="hljs-string">"3.6"</span>
 install:
   <span class="hljs-comment"># (...)</span>
 before_script:
   - echo <span class="hljs-string">"DEBUG=False"</span> &gt;&gt; .env
   - echo <span class="hljs-string">"SECRET_KEY=$SECRET_KEY"</span> &gt;&gt; .env
</code></pre>
<p>Como pode ver, eu usei o comando echo para inserir a saída que coloquei em aspas duplas no arquivo .env. No caso do SECRET_KEY, eu fui nas configurações do job no Travis CI e coloquei o SECRET_KEY, para justamnete não exibir no log, já que fiz isso em um projeto aberto no Github. Dessa forma, o decouple irá ler o arquivo recém-criado e efetuar o que desejar no seu projeto (executar os testes, etc).</p>
<p>Espero que isso tenha ajudado, e com isso recomendo o uso desse pacote.</p>
<p>Até mais!</p>
]]></content:encoded></item></channel></rss>