<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>apolzek</title><link>https://apolzek.github.io/</link><description>Sistemas em produção, observabilidade e engenharia de confiabilidade. Documentando experiências técnicas e, fora da produção, explorando filosofia e reflexões sobre a vida.</description><generator>Hugo -- gohugo.io</generator><language>pt-BR</language><lastBuildDate>Tue, 13 Oct 2026 20:08:45 GMT</lastBuildDate><atom:link href="https://apolzek.github.io/index.xml" rel="self" type="application/rss+xml"/><item><title>Diálogo sobre o Mentor e a Confiança</title><link>https://apolzek.github.io/2026/10/12/dialogo-sobre-o-mentor-e-a-confianca/</link><guid isPermaLink="true">https://apolzek.github.io/2026/10/12/dialogo-sobre-o-mentor-e-a-confianca/</guid><pubDate>Mon, 12 Oct 2026 00:00:00 GMT</pubDate><description>&lt;p&gt;&lt;em&gt;Cenário: fim de tarde na ágora. Glauco está sentado num degrau, com dois rolos de papiro no colo, sem desenrolar nenhum. Sócrates se aproxima e senta ao lado.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SÓCRATES:&lt;/strong&gt; Dois papiros e nenhum aberto. Ou os dois são ruins, ou tu não sabes qual ler primeiro.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GLAUCO:&lt;/strong&gt; Nenhum dos dois, Sócrates. São bons demais, e esse é o problema. Este é de Dámon, com quem estudo há três anos. Aquele é de Teófilo, que conheci no inverno. Nunca ouvi ninguém explicar as coisas como esses dois.&lt;/p&gt;</description><content:encoded><![CDATA[<p><em>Cenário: fim de tarde na ágora. Glauco está sentado num degrau, com dois rolos de papiro no colo, sem desenrolar nenhum. Sócrates se aproxima e senta ao lado.</em></p>
<p><strong>SÓCRATES:</strong> Dois papiros e nenhum aberto. Ou os dois são ruins, ou tu não sabes qual ler primeiro.</p>
<p><strong>GLAUCO:</strong> Nenhum dos dois, Sócrates. São bons demais, e esse é o problema. Este é de Dámon, com quem estudo há três anos. Aquele é de Teófilo, que conheci no inverno. Nunca ouvi ninguém explicar as coisas como esses dois.</p>
<p><strong>SÓCRATES:</strong> E qual é a aflição de ter dois bons mestres ?</p>
<p><strong>GLAUCO:</strong> Ontem citei Teófilo diante de Dámon. Ele fez silêncio, depois disse que Teófilo é um charlatão e que eu perderia meu tempo. Hoje contei a Teófilo, e ele riu e disse que Dámon é um velho que repete o que leu aos vinte anos. Cada um deles é tudo o que admiro. E cada um despreza o outro.</p>
<p><strong>SÓCRATES:</strong> E o que tu concluis disso ?</p>
<p><strong>GLAUCO:</strong> Que um dos dois está errado, e que preciso descobrir qual, para seguir o certo.</p>
<p><strong>SÓCRATES:</strong> Diz-me primeiro uma coisa. Por que procuraste um mestre ?</p>
<p><strong>GLAUCO:</strong> Porque não se aprende sozinho. Quem já percorreu o caminho poupa os outros de tropeçar nas mesmas pedras.</p>
<p><strong>SÓCRATES:</strong> Isso é verdade, e é bom que tenhas procurado. Mas responde com honestidade: quando encontraste Dámon, foi só o caminho que te interessou, ou também o descanso ?</p>
<p><strong>GLAUCO:</strong> Que descanso ?</p>
<p><strong>SÓCRATES:</strong> O descanso de não precisar mais decidir. Ter alguém a quem perguntar &ldquo;está certo ?&rdquo; e aceitar a resposta. Dormir sabendo que, se errares, o erro foi de quem te ensinou.</p>
<p><strong>GLAUCO:</strong> Não gosto de como soa, mas sim. Havia descanso nisso.</p>
<p><strong>SÓCRATES:</strong> Não te envergonhes. Todo homem quer esse descanso, e é por isso que todo homem procura um mentor com tanta pressa. Não é só o saber que buscamos; é alguém em quem pousar o peso de julgar. E repara: enquanto tinhas um só mestre, o descanso funcionava. O que o quebrou foi o segundo.</p>
<p><strong>GLAUCO:</strong> Porque agora um diz uma coisa e o outro diz outra.</p>
<p><strong>SÓCRATES:</strong> Não. Sobre a maior parte das coisas, os dois dizem o mesmo, ou tu não admirarias ambos. O que quebrou o teu descanso foi outra coisa: descobriste que a admiração que sentes por um não obriga o outro a sentir o mesmo. Tu querias que teus mestres se amassem como tu os amas. Eles não têm esse dever.</p>
<p><strong>GLAUCO:</strong> Mas se Dámon é sábio, e diz que Teófilo é charlatão, não devo ao menos considerar ?</p>
<p><strong>SÓCRATES:</strong> Deves, e também deves considerar o contrário. Mas vamos devagar. Quando Dámon te ensina geometria, de onde vem a autoridade dele ?</p>
<p><strong>GLAUCO:</strong> De saber geometria.</p>
<p><strong>SÓCRATES:</strong> E quando ele te diz que Teófilo é charlatão, de onde vem a autoridade ?</p>
<p><strong>GLAUCO:</strong> De&hellip; conhecer Teófilo, suponho.</p>
<p><strong>SÓCRATES:</strong> E conhece ? Já o ouviu ensinar ? Já leu o que ele escreve ? Ou conhece a fama, uma frase dita numa discussão, a lembrança de uma rixa antiga que ninguém mais sabe como começou ?</p>
<p><strong>GLAUCO:</strong> Não sei. Nunca perguntei.</p>
<p><strong>SÓCRATES:</strong> Então aqui está a primeira coisa a guardar. Um homem pode ser mestre em uma arte e ser tão ignorante quanto qualquer um numa segunda. Saber geometria não torna ninguém juiz de caráter. Quando tu aceitas o juízo de Dámon sobre Teófilo com a mesma confiança com que aceitas o teorema, estás estendendo a autoridade dele para um terreno onde ele não a conquistou. E ele, se for honesto, não a reivindicaria.</p>
<p><strong>GLAUCO:</strong> Então nem um nem outro está errado sobre geometria. Estão errados um sobre o outro.</p>
<p><strong>SÓCRATES:</strong> Talvez. Ou talvez um deles tenha razão, e tu ainda não saibas. Mas repara no que acabaste de fazer: há um minuto, o problema era &ldquo;qual dos dois seguir&rdquo;. Agora é &ldquo;o que cada um sabe de fato, e sobre o quê&rdquo;. É uma pergunta melhor.</p>
<p><strong>GLAUCO:</strong> É uma pergunta que me devolve o trabalho.</p>
<p><strong>SÓCRATES:</strong> Esse trabalho sempre foi teu, Glauco. Tu só o tinhas emprestado. Já conversamos, noutra tarde, sobre a tábua da praça: o rótulo que alguém te cola não te dispensa de examinar. O mesmo vale para o rótulo que um mestre cola no outro. &ldquo;Charlatão&rdquo; e &ldquo;velho que repete&rdquo; são pedras atiradas, não razões. Pede as razões. Se vierem, pesa-as. Se não vierem, deixa as pedras no chão.</p>
<p><strong>GLAUCO:</strong> E se eu pesar e concluir que Dámon, que tanto me ensinou, errou sobre Teófilo ? O que faço com isso ?</p>
<p><strong>SÓCRATES:</strong> O que fazes quando o teu cavalo tropeça ?</p>
<p><strong>GLAUCO:</strong> Levanto-o e sigo. Não vendo o cavalo.</p>
<p><strong>SÓCRATES:</strong> E por que venderias o mestre ? Glauco, ouve bem isto, porque é a parte que mais custa aos jovens: <em>o teu mestre vai errar</em>. Não &ldquo;pode errar&rdquo;, vai. Vai errar sobre pessoas, vai errar sobre fatos, vai ter dias de ciúme, de cansaço e de teimosia. E quando isso acontecer, tu terás duas saídas erradas e uma certa. A primeira errada é fingir que não viste, para proteger o descanso. A segunda errada é derrubar o homem inteiro, porque uma coluna rachou. A certa é mais difícil: continuar a aprender geometria com quem sabe geometria, e guardar para ti o juízo sobre as pessoas.</p>
<p><strong>GLAUCO:</strong> Soa como ter menos confiança nele.</p>
<p><strong>SÓCRATES:</strong> Soa como ter a confiança certa, em vez da confiança inteira. A confiança inteira é a que se dá aos deuses, e nem a esses tanto. Dar a um homem a confiança que cabe a um deus não é honrá-lo. É condená-lo a te decepcionar, porque ele não tem como pagar a dívida que tu mesmo inventaste.</p>
<p><strong>GLAUCO:</strong> Então o mentor é um homem como eu.</p>
<p><strong>SÓCRATES:</strong> Que foi um pouco mais longe num caminho. Só isso, e isso já é muito. Repara que é justamente o erro que te permite reconhecer nele um igual. Um mestre que nunca errasse não seria mestre, seria um oráculo, e diante de oráculos não se aprende, obedece-se. Um homem que erra e corrige, que mostra onde tropeçou, te ensina duas vezes: ensina a arte e ensina que a arte cabe numa vida humana. O erro é a prova de que o caminho que ele percorreu é um caminho que tu também podes percorrer.</p>
<p><strong>GLAUCO:</strong> Nunca tinha pensado que o erro do mestre fosse um presente.</p>
<p><strong>SÓCRATES:</strong> Não é um presente, é um fato. Mas é um fato que te liberta, se o aceitares. Enquanto o teu mestre for perfeito, tu serás eternamente aluno. No dia em que o vires errar, e continuares a respeitá-lo mesmo assim, terás começado a ter opinião própria. E ter opinião própria sobre as pessoas, inclusive sobre quem mais admiras, é o começo de ser alguém, e não a sombra de alguém.</p>
<p><strong>GLAUCO:</strong> E Teófilo ? Continuo a ouvi-lo ?</p>
<p><strong>SÓCRATES:</strong> Continua a ouvir os dois. Aprende de cada um o que cada um sabe. E quando um falar mal do outro, faz o que farias com qualquer outra afirmação: pergunta &ldquo;por quê ?&rdquo;, escuta, e decide tu. É possível que um dia descubras que um deles tinha razão. É possível que descubras que os dois estavam sendo pequenos, como homens às vezes são. Nos dois casos terás aprendido algo que nenhum deles poderia te ensinar: que os teus olhos também valem.</p>
<p><strong>GLAUCO:</strong> E se os meus olhos errarem ?</p>
<p><strong>SÓCRATES:</strong> Vão errar, e então serás como eles. <strong>É isso que nos faz humanos, Glauco: não a ausência do erro, mas o dever de julgar mesmo sabendo que podemos errar, e a decência de continuar a honrar quem errou antes de nós</strong>.</p>
<p>..então Glauco guardou os dois papiros na mesma sacola, Sócrates comentou que a sacola parecia mais leve, e os dois foram tomar uma cerveja.</p>
<h2>Glossário<span class="hx:absolute hx:-mt-20" id="glossário"></span>
    <a href="#gloss%c3%a1rio" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li><em>ágora</em>: praça central das cidades gregas, onde se reunia o povo</li>
<li><em>papiro</em>: material em que se escrevia antes do papel, enrolado em rolos</li>
<li><em>oráculo</em>: lugar ou pessoa que, na Grécia antiga, dava respostas tidas como vindas dos deuses</li>
</ul>
<h2>Nota<span class="hx:absolute hx:-mt-20" id="nota"></span>
    <a href="#nota" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Este diálogo é uma continuação do <a href="/2026/04/11/dialogo-sobre-rotulos-e-verdade/">Diálogo sobre os Rótulos e a Verdade</a>. Lá, a questão era o rótulo que os outros colam em nós. Aqui, é o rótulo que as pessoas que admiramos colam umas nas outras.</p>
<p>Todo mundo procura um mentor, e a necessidade é legítima: ninguém aprende tudo sozinho. O problema não está em confiar, está na quantidade. É muito fácil entregar a alguém que admiramos não só o que ele sabe, mas também o que ele acha, inclusive sobre outras pessoas. E quando duas pessoas que respeitamos não se respeitam, o conforto de ter um guia desaba, porque descobrimos que o juízo delas sobre o mundo não era um só.</p>
<p>A saída não é escolher um lado nem abandonar os dois. É aceitar que todos erram, o mentor incluído, e que ter opinião própria sobre as pessoas, formada com as nossas razões e os nossos olhos, não é desrespeito. É a única forma de respeito que não exige que o outro seja mais do que humano.</p>
]]></content:encoded><category>filosofia</category><category>dialogos</category></item><item><title>Vontade, excelência e cardiologia</title><link>https://apolzek.github.io/2026/10/09/vontade-excelencia-e-cardiologia/</link><guid isPermaLink="true">https://apolzek.github.io/2026/10/09/vontade-excelencia-e-cardiologia/</guid><pubDate>Fri, 09 Oct 2026 00:00:00 GMT</pubDate><description>&lt;p&gt;Tenho meus heróis nacionais. Brasileiros que, pelo patriotismo, pela determinação e pela maneira como conduziram suas vidas, se tornaram exemplos daquilo que um dia quero ser.&lt;/p&gt;
&lt;p&gt;Sempre admirei a medicina, especialmente aqueles médicos que dedicam seus esforços, seu tempo e cada célula do próprio corpo à busca pela excelência. Entre as especialidades, uma sempre despertou minha admiração: a cardiologia.&lt;/p&gt;
&lt;p&gt;Um dos nomes que carrego como referência é o de &lt;strong&gt;Enéas Carneiro&lt;/strong&gt;. Muitos o admiravam por sua inteligência, mas eu sempre enxerguei nele algo que considero ainda mais importante e menos questionável: &lt;strong&gt;a vontade&lt;/strong&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Tenho meus heróis nacionais. Brasileiros que, pelo patriotismo, pela determinação e pela maneira como conduziram suas vidas, se tornaram exemplos daquilo que um dia quero ser.</p>
<p>Sempre admirei a medicina, especialmente aqueles médicos que dedicam seus esforços, seu tempo e cada célula do próprio corpo à busca pela excelência. Entre as especialidades, uma sempre despertou minha admiração: a cardiologia.</p>
<p>Um dos nomes que carrego como referência é o de <strong>Enéas Carneiro</strong>. Muitos o admiravam por sua inteligência, mas eu sempre enxerguei nele algo que considero ainda mais importante e menos questionável: <strong>a vontade</strong>.</p>
<p>Enéas tinha vontade. Sabia o que queria, defendia suas convicções e desejava um Brasil melhor. Além disso, dedicou parte de sua vida ao estudo do coração, tornando-se cardiologista.</p>
<p>Mas a vida encontrou uma maneira de transformar essa admiração em algo muito mais profundo.</p>
<p>No dia <strong>13 de novembro de 2025</strong>, nasceu minha filha, Luna, com uma cardiopatia congênita grave. Ela precisou passar por uma cirurgia cardíaca no Hospital Vila da Serra, em Nova Lima, Minas Gerais.</p>
<p>Naquele momento, tudo aquilo que eu admirava na medicina deixou de ser algo distante, quase abstrato, e passou a fazer parte da minha própria história.</p>
<p><strong>O que antes eu admirava, hoje eu venero.</strong></p>
<p>Creio que Deus abençoou a equipe de cardiologistas daquele hospital. Homens e mulheres que dedicaram suas vidas ao conhecimento, à ciência e ao aperfeiçoamento de suas habilidades e que, por meio de tudo aquilo que o ser humano foi capaz de aprender e desenvolver, receberam a oportunidade de salvar a vida da minha filha.</p>
<p>É difícil encontrar palavras que expressem a dimensão da minha gratidão. Existe algo profundamente admirável em alguém que dedica a própria existência a adquirir conhecimentos e habilidades que, um dia, poderão significar a diferença entre a vida e a morte de outra pessoa.</p>
<p>Deixo aqui meu mais profundo respeito e minha eterna gratidão aos profissionais da saúde, especialmente à equipe que cuidou da Luna, e a todas as pessoas que estão dentro dos hospitais movidas por uma intenção verdadeira e por um propósito maior: <strong>salvar vidas</strong>.</p>
<p>Se antes eu admirava a medicina pelo conhecimento e pela excelência de seus profissionais, hoje a admiro também por algo que nenhuma teoria seria capaz de me ensinar: a oportunidade de continuar vendo minha filha crescer.</p>
<blockquote>
  <p>&ldquo;O dado mais importante que separa o ser humano de todos os seus irmãos e primos da escala filogenética é o conhecimento; só o conhecimento liberta o homem, só através do conhecimento o homem é livre e, em sendo livre, ele pode aspirar a uma condição melhor de vida para ele e todos os seus semelhantes. Só consigo entender uma sociedade na qual o conhecimento seja a razão de ser precípua que o governo dá para a formação do cidadão. Minha mensagem é positiva: é de que o homem tem de saber, conhecer e, em conhecendo, ele é livre.&rdquo;</p>
<p>— Dr. Enéas Ferreira Carneiro (Rio Branco, 5 de novembro de 1938 — Rio de Janeiro, 6 de maio de 2007)</p>

</blockquote>
]]></content:encoded><category>pessoal</category><category>saude</category></item><item><title>Ferramentas do stack de dados e ML por categoria</title><link>https://apolzek.github.io/2026/09/01/data-and-ml-stack-tools/</link><guid isPermaLink="true">https://apolzek.github.io/2026/09/01/data-and-ml-stack-tools/</guid><pubDate>Tue, 01 Sep 2026 12:00:00 GMT</pubDate><description>&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Categoria&lt;/th&gt;
&lt;th&gt;Ferramentas / Tecnologias&lt;/th&gt;
&lt;th&gt;Uso principal&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Machine Learning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MLflow, Kubeflow, Feast, Ray, TensorFlow, PyTorch, Scikit-learn, XGBoost, LightGBM&lt;/td&gt;
&lt;td&gt;Treino, tracking, serving, feature store e MLOps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Science / Analytics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Python, Jupyter, Pandas, Polars, NumPy, SciPy, R, Apache Arrow&lt;/td&gt;
&lt;td&gt;Exploração de dados, análise estatística e preparação de dados&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;BI / Analytics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Apache Superset, Metabase, Grafana, Power BI, Tableau&lt;/td&gt;
&lt;td&gt;Dashboards, exploração e visualização de dados&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Engenharia de Dados&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Apache Spark, Apache Flink, Apache Beam, Apache Kafka, dbt&lt;/td&gt;
&lt;td&gt;Processamento de dados, pipelines e transformações&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ingestão / CDC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Apache Kafka, Debezium, Kafka Connect, Apache NiFi, Airbyte, Fivetran&lt;/td&gt;
&lt;td&gt;Ingestão de dados, CDC e integração de dados&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Orquestração de workflows&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Apache Airflow, Dagster, Prefect, Argo Workflows&lt;/td&gt;
&lt;td&gt;Orquestração de pipelines e workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transformação de dados&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;dbt, Apache Spark, SQLMesh, Dataform&lt;/td&gt;
&lt;td&gt;Transformação, modelagem e ELT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Qualidade de dados&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Great Expectations, Soda, dbt Tests, Deequ&lt;/td&gt;
&lt;td&gt;Validação e checagens de qualidade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Catálogo / Governança&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OpenMetadata, DataHub, Apache Atlas, Amundsen, OpenLineage&lt;/td&gt;
&lt;td&gt;Catálogo de dados, linhagem e governança&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Armazenamento do data lake&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Amazon S3, MinIO, Google Cloud Storage, Azure Data Lake Storage&lt;/td&gt;
&lt;td&gt;Object storage e data lake&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Formatos de arquivo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Apache Parquet, Apache ORC, Apache Avro, Apache Arrow&lt;/td&gt;
&lt;td&gt;Formatos de armazenamento e intercâmbio de dados&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Formatos de tabela abertos&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Apache Iceberg, Apache Hudi, Delta Lake, Apache Paimon&lt;/td&gt;
&lt;td&gt;Tabelas ACID, evolução de schema, snapshots e time travel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Catálogo de lakehouse&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Apache Polaris, Project Nessie, AWS Glue Catalog, Hive Metastore&lt;/td&gt;
&lt;td&gt;Catálogo de tabelas e gestão de metadados&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Engines de consulta&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Trino, Presto, DuckDB, ClickHouse, Apache Doris, StarRocks&lt;/td&gt;
&lt;td&gt;Analytics em SQL e OLAP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Plataformas de lakehouse&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Databricks, Dremio, Starburst, Snowflake&lt;/td&gt;
&lt;td&gt;Plataformas de data lakehouse e analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Feature Store&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Feast, Hopsworks, Tecton&lt;/td&gt;
&lt;td&gt;Gestão e serving de features de ML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pipelines de ML&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Kubeflow Pipelines, MLflow, Airflow, Metaflow, Flyte&lt;/td&gt;
&lt;td&gt;Pipelines de machine learning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Model Serving&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;KServe, Seldon, NVIDIA Triton, Ray Serve, BentoML, vLLM&lt;/td&gt;
&lt;td&gt;Inferência e serving de modelos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Experiment Tracking&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MLflow, Weights &amp;amp; Biases, Neptune, Comet&lt;/td&gt;
&lt;td&gt;Acompanhamento de experimentos e métricas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Model Registry&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MLflow, Kubeflow, Vertex AI, SageMaker, Azure ML&lt;/td&gt;
&lt;td&gt;Versionamento e ciclo de vida de modelos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bancos vetoriais&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Qdrant, Milvus, Weaviate, pgvector, Pinecone&lt;/td&gt;
&lt;td&gt;Embeddings, busca semântica e RAG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Streaming / Processamento de eventos&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Apache Kafka, Apache Flink, Redpanda, Apache Pulsar&lt;/td&gt;
&lt;td&gt;Processamento de dados em tempo real&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Warehouse&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Snowflake, BigQuery, Redshift, ClickHouse, DuckDB&lt;/td&gt;
&lt;td&gt;Analytics estruturado e OLAP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Observabilidade de dados&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OpenTelemetry, Grafana, Prometheus, OpenLineage, Marquez&lt;/td&gt;
&lt;td&gt;Observabilidade de dados e pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Arquiteturas de referência&lt;span class="hx:absolute hx:-mt-20" id="arquiteturas-de-referência"&gt;&lt;/span&gt;
&lt;a href="#arquiteturas-de-refer%c3%aancia" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Três jeitos de ligar as ferramentas acima num stack que funciona. Todas têm as
mesmas três camadas, ingestão, processamento e armazenamento, mais o que quer
que consuma o resultado.&lt;/p&gt;</description><content:encoded><![CDATA[<table>
  <thead>
      <tr>
          <th>Categoria</th>
          <th>Ferramentas / Tecnologias</th>
          <th>Uso principal</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Machine Learning</strong></td>
          <td>MLflow, Kubeflow, Feast, Ray, TensorFlow, PyTorch, Scikit-learn, XGBoost, LightGBM</td>
          <td>Treino, tracking, serving, feature store e MLOps</td>
      </tr>
      <tr>
          <td><strong>Data Science / Analytics</strong></td>
          <td>Python, Jupyter, Pandas, Polars, NumPy, SciPy, R, Apache Arrow</td>
          <td>Exploração de dados, análise estatística e preparação de dados</td>
      </tr>
      <tr>
          <td><strong>BI / Analytics</strong></td>
          <td>Apache Superset, Metabase, Grafana, Power BI, Tableau</td>
          <td>Dashboards, exploração e visualização de dados</td>
      </tr>
      <tr>
          <td><strong>Engenharia de Dados</strong></td>
          <td>Apache Spark, Apache Flink, Apache Beam, Apache Kafka, dbt</td>
          <td>Processamento de dados, pipelines e transformações</td>
      </tr>
      <tr>
          <td><strong>Ingestão / CDC</strong></td>
          <td>Apache Kafka, Debezium, Kafka Connect, Apache NiFi, Airbyte, Fivetran</td>
          <td>Ingestão de dados, CDC e integração de dados</td>
      </tr>
      <tr>
          <td><strong>Orquestração de workflows</strong></td>
          <td>Apache Airflow, Dagster, Prefect, Argo Workflows</td>
          <td>Orquestração de pipelines e workflows</td>
      </tr>
      <tr>
          <td><strong>Transformação de dados</strong></td>
          <td>dbt, Apache Spark, SQLMesh, Dataform</td>
          <td>Transformação, modelagem e ELT</td>
      </tr>
      <tr>
          <td><strong>Qualidade de dados</strong></td>
          <td>Great Expectations, Soda, dbt Tests, Deequ</td>
          <td>Validação e checagens de qualidade</td>
      </tr>
      <tr>
          <td><strong>Catálogo / Governança</strong></td>
          <td>OpenMetadata, DataHub, Apache Atlas, Amundsen, OpenLineage</td>
          <td>Catálogo de dados, linhagem e governança</td>
      </tr>
      <tr>
          <td><strong>Armazenamento do data lake</strong></td>
          <td>Amazon S3, MinIO, Google Cloud Storage, Azure Data Lake Storage</td>
          <td>Object storage e data lake</td>
      </tr>
      <tr>
          <td><strong>Formatos de arquivo</strong></td>
          <td>Apache Parquet, Apache ORC, Apache Avro, Apache Arrow</td>
          <td>Formatos de armazenamento e intercâmbio de dados</td>
      </tr>
      <tr>
          <td><strong>Formatos de tabela abertos</strong></td>
          <td>Apache Iceberg, Apache Hudi, Delta Lake, Apache Paimon</td>
          <td>Tabelas ACID, evolução de schema, snapshots e time travel</td>
      </tr>
      <tr>
          <td><strong>Catálogo de lakehouse</strong></td>
          <td>Apache Polaris, Project Nessie, AWS Glue Catalog, Hive Metastore</td>
          <td>Catálogo de tabelas e gestão de metadados</td>
      </tr>
      <tr>
          <td><strong>Engines de consulta</strong></td>
          <td>Trino, Presto, DuckDB, ClickHouse, Apache Doris, StarRocks</td>
          <td>Analytics em SQL e OLAP</td>
      </tr>
      <tr>
          <td><strong>Plataformas de lakehouse</strong></td>
          <td>Databricks, Dremio, Starburst, Snowflake</td>
          <td>Plataformas de data lakehouse e analytics</td>
      </tr>
      <tr>
          <td><strong>Feature Store</strong></td>
          <td>Feast, Hopsworks, Tecton</td>
          <td>Gestão e serving de features de ML</td>
      </tr>
      <tr>
          <td><strong>Pipelines de ML</strong></td>
          <td>Kubeflow Pipelines, MLflow, Airflow, Metaflow, Flyte</td>
          <td>Pipelines de machine learning</td>
      </tr>
      <tr>
          <td><strong>Model Serving</strong></td>
          <td>KServe, Seldon, NVIDIA Triton, Ray Serve, BentoML, vLLM</td>
          <td>Inferência e serving de modelos</td>
      </tr>
      <tr>
          <td><strong>Experiment Tracking</strong></td>
          <td>MLflow, Weights &amp; Biases, Neptune, Comet</td>
          <td>Acompanhamento de experimentos e métricas</td>
      </tr>
      <tr>
          <td><strong>Model Registry</strong></td>
          <td>MLflow, Kubeflow, Vertex AI, SageMaker, Azure ML</td>
          <td>Versionamento e ciclo de vida de modelos</td>
      </tr>
      <tr>
          <td><strong>Bancos vetoriais</strong></td>
          <td>Qdrant, Milvus, Weaviate, pgvector, Pinecone</td>
          <td>Embeddings, busca semântica e RAG</td>
      </tr>
      <tr>
          <td><strong>Streaming / Processamento de eventos</strong></td>
          <td>Apache Kafka, Apache Flink, Redpanda, Apache Pulsar</td>
          <td>Processamento de dados em tempo real</td>
      </tr>
      <tr>
          <td><strong>Data Warehouse</strong></td>
          <td>Snowflake, BigQuery, Redshift, ClickHouse, DuckDB</td>
          <td>Analytics estruturado e OLAP</td>
      </tr>
      <tr>
          <td><strong>Observabilidade de dados</strong></td>
          <td>OpenTelemetry, Grafana, Prometheus, OpenLineage, Marquez</td>
          <td>Observabilidade de dados e pipelines</td>
      </tr>
  </tbody>
</table>
<h2>Arquiteturas de referência<span class="hx:absolute hx:-mt-20" id="arquiteturas-de-referência"></span>
    <a href="#arquiteturas-de-refer%c3%aancia" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Três jeitos de ligar as ferramentas acima num stack que funciona. Todas têm as
mesmas três camadas, ingestão, processamento e armazenamento, mais o que quer
que consuma o resultado.</p>
<h3>1. Lakehouse em batch<span class="hx:absolute hx:-mt-20" id="1-lakehouse-em-batch"></span>
    <a href="#1-lakehouse-em-batch" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>ELT noturno e de hora em hora. O padrão quando a latência é medida em horas.</p>
<pre class="mermaid hx:mt-6">
  flowchart TB
  subgraph ing[&#34;Ingestão&#34;]
    airbyte[&#34;Airbyte&#34;]
    debezium[&#34;Debezium + Kafka Connect&#34;]
  end
  subgraph proc[&#34;Processamento&#34;]
    spark[&#34;Apache Spark&#34;]
    dbt[&#34;dbt&#34;]
    ge[&#34;Great Expectations&#34;]
  end
  subgraph sto[&#34;Armazenamento&#34;]
    iceberg[&#34;Apache Iceberg&#34;]
    s3[&#34;MinIO / S3&#34;]
    polaris[&#34;Apache Polaris&#34;]
  end
  subgraph out[&#34;Consumo&#34;]
    trino[&#34;Trino&#34;]
    superset[&#34;Apache Superset&#34;]
  end

  airbyte --&gt; spark
  debezium --&gt; spark
  spark --&gt; dbt --&gt; ge --&gt; iceberg
  s3 -.- iceberg
  polaris -.- iceberg
  iceberg --&gt; trino --&gt; superset
</pre><p>O Apache Airflow fica fora do desenho, disparando cada seta acima.</p>
<h3>2. Streaming<span class="hx:absolute hx:-mt-20" id="2-streaming"></span>
    <a href="#2-streaming" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Segundos em vez de horas. Dois ramos saindo de um único job: a tabela para o
histórico, o store OLAP para o dashboard.</p>
<pre class="mermaid hx:mt-6">
  flowchart TB
  subgraph ing[&#34;Ingestão&#34;]
    debezium[&#34;Debezium&#34;]
    kafka[&#34;Apache Kafka&#34;]
  end
  subgraph proc[&#34;Processamento&#34;]
    flink[&#34;Apache Flink&#34;]
  end
  subgraph sto[&#34;Armazenamento&#34;]
    paimon[&#34;Apache Paimon no S3&#34;]
    clickhouse[&#34;ClickHouse&#34;]
  end
  subgraph out[&#34;Consumo&#34;]
    trino[&#34;Trino&#34;]
    grafana[&#34;Grafana&#34;]
  end

  debezium --&gt; kafka --&gt; flink
  flink --&gt; paimon --&gt; trino
  flink --&gt; clickhouse --&gt; grafana
</pre><p>O Airflow continua necessário aqui, não para agendar o job, mas para rodar a
compactação e a expiração de snapshots que a tabela precisa, porque o job nunca
para de escrever.</p>
<h3>3. ML e inferência<span class="hx:absolute hx:-mt-20" id="3-ml-e-inferência"></span>
    <a href="#3-ml-e-infer%c3%aancia" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Features escritas pelo mesmo pipeline que alimenta o treino, e um modelo servido
a partir de um registry.</p>
<pre class="mermaid hx:mt-6">
  flowchart TB
  subgraph ing[&#34;Ingestão&#34;]
    kafka[&#34;Apache Kafka&#34;]
  end
  subgraph proc[&#34;Processamento&#34;]
    flink[&#34;Apache Flink&#34;]
    spark[&#34;Apache Spark&#34;]
  end
  subgraph sto[&#34;Armazenamento&#34;]
    feast[&#34;Feast&#34;]
    delta[&#34;Delta Lake no S3&#34;]
    qdrant[&#34;Qdrant&#34;]
  end
  subgraph train[&#34;Treino&#34;]
    ray[&#34;Ray&#34;]
    mlflow[&#34;MLflow Registry&#34;]
  end
  subgraph serve[&#34;Serving&#34;]
    kserve[&#34;KServe&#34;]
    vllm[&#34;vLLM&#34;]
  end

  kafka --&gt; flink
  kafka --&gt; spark
  flink --&gt; feast
  flink --&gt; qdrant
  spark --&gt; delta
  delta --&gt; ray --&gt; mlflow --&gt; kserve
  feast --&gt; kserve
  qdrant --&gt; vllm
</pre><h2>Repositórios oficiais<span class="hx:absolute hx:-mt-20" id="repositórios-oficiais"></span>
    <a href="#reposit%c3%b3rios-oficiais" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li><a href="https://github.com/airbytehq/airbyte"target="_blank" rel="noopener">Airbyte</a>: ELT baseado em conectores para carregar dados num warehouse ou lake.</li>
<li><a href="https://github.com/amundsen-io/amundsen"target="_blank" rel="noopener">Amundsen</a>: Descoberta de dados e busca de metadados para analistas.</li>
<li><a href="https://github.com/apache/airflow"target="_blank" rel="noopener">Apache Airflow</a>: Agenda e monitora pipelines escritos como DAGs em Python.</li>
<li><a href="https://github.com/apache/arrow"target="_blank" rel="noopener">Apache Arrow</a>: Formato colunar em memória compartilhado entre engines e linguagens.</li>
<li><a href="https://github.com/apache/atlas"target="_blank" rel="noopener">Apache Atlas</a>: Governança de metadados e linhagem para o ecossistema Hadoop.</li>
<li><a href="https://github.com/apache/avro"target="_blank" rel="noopener">Apache Avro</a>: Formato de serialização orientado a linhas que carrega o próprio schema.</li>
<li><a href="https://github.com/apache/beam"target="_blank" rel="noopener">Apache Beam</a>: Um único modelo de pipeline que roda em várias engines de batch e streaming.</li>
<li><a href="https://github.com/apache/doris"target="_blank" rel="noopener">Apache Doris</a>: Banco analítico MPP voltado a relatórios em tempo real.</li>
<li><a href="https://github.com/apache/flink"target="_blank" rel="noopener">Apache Flink</a>: Processamento de streams com estado, event time e exactly-once.</li>
<li><a href="https://github.com/apache/hudi"target="_blank" rel="noopener">Apache Hudi</a>: Formato de tabela de lake com upserts e leituras incrementais.</li>
<li><a href="https://github.com/apache/iceberg"target="_blank" rel="noopener">Apache Iceberg</a>: Formato de tabela aberto com commits ACID, evolução de schema e time travel.</li>
<li><a href="https://github.com/apache/kafka"target="_blank" rel="noopener">Apache Kafka</a>: Log distribuído para publicar e consumir streams de eventos.</li>
<li><a href="https://github.com/apache/nifi"target="_blank" rel="noopener">Apache NiFi</a>: Roteamento e transformação baseados em fluxo, com editor visual.</li>
<li><a href="https://github.com/apache/orc"target="_blank" rel="noopener">Apache ORC</a>: Formato de arquivo colunar feito para leituras grandes no estilo Hive.</li>
<li><a href="https://github.com/apache/paimon"target="_blank" rel="noopener">Apache Paimon</a>: Formato de tabela de lake projetado para ser escrito continuamente pelo Flink.</li>
<li><a href="https://github.com/apache/parquet-format"target="_blank" rel="noopener">Apache Parquet</a>: O formato de arquivo colunar em que a maior parte do lake é armazenada.</li>
<li><a href="https://github.com/apache/polaris"target="_blank" rel="noopener">Apache Polaris</a>: Catálogo aberto para que várias engines vejam as mesmas tabelas Iceberg.</li>
<li><a href="https://github.com/apache/pulsar"target="_blank" rel="noopener">Apache Pulsar</a>: Mensageria e streaming com armazenamento separado do serving.</li>
<li><a href="https://github.com/apache/spark"target="_blank" rel="noopener">Apache Spark</a>: Engine geral para batch, SQL e ML sobre grandes volumes de dados.</li>
<li><a href="https://github.com/apache/superset"target="_blank" rel="noopener">Apache Superset</a>: BI web com dashboards e exploração em SQL.</li>
<li><a href="https://github.com/argoproj/argo-workflows"target="_blank" rel="noopener">Argo Workflows</a>: Roda workflows nativos de contêiner como objetos do Kubernetes.</li>
<li><a href="https://github.com/bentoml/BentoML"target="_blank" rel="noopener">BentoML</a>: Empacota um modelo e suas dependências num serviço.</li>
<li><a href="https://github.com/ClickHouse/ClickHouse"target="_blank" rel="noopener">ClickHouse</a>: Column store para consultas analíticas rápidas em escala.</li>
<li><a href="https://github.com/dagster-io/dagster"target="_blank" rel="noopener">Dagster</a>: Orquestrador construído em torno de data assets em vez de tarefas.</li>
<li><a href="https://github.com/dataform-co/dataform"target="_blank" rel="noopener">Dataform</a>: Workflows de transformação em SQL com gestão de dependências.</li>
<li><a href="https://github.com/datahub-project/datahub"target="_blank" rel="noopener">DataHub</a>: Plataforma de metadados para catálogo, linhagem e ownership.</li>
<li><a href="https://github.com/dbt-labs/dbt-core"target="_blank" rel="noopener">dbt</a>: Transforma selects em SQL em modelos testados, documentados e versionados.</li>
<li><a href="https://github.com/debezium/debezium"target="_blank" rel="noopener">Debezium</a>: Lê os logs de mudança do banco e os publica como eventos.</li>
<li><a href="https://github.com/awslabs/deequ"target="_blank" rel="noopener">Deequ</a>: Checagens de qualidade de dados expressas como jobs Spark.</li>
<li><a href="https://github.com/delta-io/delta"target="_blank" rel="noopener">Delta Lake</a>: Camada de armazenamento transacional sobre arquivos Parquet.</li>
<li><a href="https://github.com/dremio/dremio-oss"target="_blank" rel="noopener">Dremio</a>: Engine de consulta e camada semântica sobre o armazenamento do lake.</li>
<li><a href="https://github.com/duckdb/duckdb"target="_blank" rel="noopener">DuckDB</a>: Banco analítico que roda dentro do seu processo, sem servidor.</li>
<li><a href="https://github.com/feast-dev/feast"target="_blank" rel="noopener">Feast</a>: Feature store que serve as mesmas features para treino e inferência.</li>
<li><a href="https://github.com/flyteorg/flyte"target="_blank" rel="noopener">Flyte</a>: Workflows tipados e versionados para dados e ML no Kubernetes.</li>
<li><a href="https://github.com/grafana/grafana"target="_blank" rel="noopener">Grafana</a>: Dashboards sobre métricas, logs e traces.</li>
<li><a href="https://github.com/fivetran/great_expectations"target="_blank" rel="noopener">Great Expectations</a>: Declara expectativas sobre os dados e as valida.</li>
<li><a href="https://github.com/apache/hive"target="_blank" rel="noopener">Hive Metastore</a>: O catálogo de tabelas e partições que muitas engines ainda leem.</li>
<li><a href="https://github.com/logicalclocks/hopsworks"target="_blank" rel="noopener">Hopsworks</a>: Feature store e plataforma de ML.</li>
<li><a href="https://github.com/jupyter/notebook"target="_blank" rel="noopener">Jupyter</a>: Notebooks para análise interativa.</li>
<li><a href="https://github.com/kserve/kserve"target="_blank" rel="noopener">KServe</a>: Serve modelos no Kubernetes com autoscaling e canaries.</li>
<li><a href="https://github.com/kubeflow/kubeflow"target="_blank" rel="noopener">Kubeflow</a>: Plataforma de ML montada a partir de componentes do Kubernetes.</li>
<li><a href="https://github.com/kubeflow/pipelines"target="_blank" rel="noopener">Kubeflow Pipelines</a>: Compõe e executa pipelines de ML como contêineres.</li>
<li><a href="https://github.com/lightgbm-org/LightGBM"target="_blank" rel="noopener">LightGBM</a>: Gradient boosting otimizado para velocidade em dados tabulares grandes.</li>
<li><a href="https://github.com/MarquezProject/marquez"target="_blank" rel="noopener">Marquez</a>: Servidor de referência que coleta e armazena eventos OpenLineage.</li>
<li><a href="https://github.com/metabase/metabase"target="_blank" rel="noopener">Metabase</a>: BI que deixa quem não é engenheiro fazer perguntas ao banco.</li>
<li><a href="https://github.com/Netflix/metaflow"target="_blank" rel="noopener">Metaflow</a>: Workflows em Python para data science, da Netflix.</li>
<li><a href="https://github.com/milvus-io/milvus"target="_blank" rel="noopener">Milvus</a>: Banco vetorial para busca por similaridade em escala.</li>
<li><a href="https://github.com/minio/minio"target="_blank" rel="noopener">MinIO</a>: Object storage compatível com S3 que você mesmo roda.</li>
<li><a href="https://github.com/mlflow/mlflow"target="_blank" rel="noopener">MLflow</a>: Acompanha experimentos, empacota modelos e mantém o registry.</li>
<li><a href="https://github.com/numpy/numpy"target="_blank" rel="noopener">NumPy</a>: Arrays e computação numérica, a base do stack Python.</li>
<li><a href="https://github.com/triton-inference-server/server"target="_blank" rel="noopener">NVIDIA Triton</a>: Servidor de inferência para modelos em GPU e CPU.</li>
<li><a href="https://github.com/OpenLineage/OpenLineage"target="_blank" rel="noopener">OpenLineage</a>: Padrão aberto para pipelines reportarem o que leram e escreveram.</li>
<li><a href="https://github.com/open-metadata/OpenMetadata"target="_blank" rel="noopener">OpenMetadata</a>: Catálogo, linhagem, qualidade e governança num lugar só.</li>
<li><a href="https://github.com/open-telemetry/opentelemetry-specification"target="_blank" rel="noopener">OpenTelemetry</a>: Padrão neutro de fornecedor para traces, métricas e logs.</li>
<li><a href="https://github.com/pandas-dev/pandas"target="_blank" rel="noopener">Pandas</a>: DataFrames para manipulação de dados em Python.</li>
<li><a href="https://github.com/pgvector/pgvector"target="_blank" rel="noopener">pgvector</a>: Tipos e índices vetoriais dentro do PostgreSQL.</li>
<li><a href="https://github.com/pola-rs/polars"target="_blank" rel="noopener">Polars</a>: DataFrames rápidos escritos em Rust, com engine de consulta lazy.</li>
<li><a href="https://github.com/PrefectHQ/prefect"target="_blank" rel="noopener">Prefect</a>: Orquestração para workflows em Python, feita para execuções dinâmicas.</li>
<li><a href="https://github.com/prestodb/presto"target="_blank" rel="noopener">Presto</a>: Engine SQL distribuída que consulta os dados onde eles estão.</li>
<li><a href="https://github.com/projectnessie/nessie"target="_blank" rel="noopener">Project Nessie</a>: Branches e commits no estilo Git para tabelas do lake.</li>
<li><a href="https://github.com/prometheus/prometheus"target="_blank" rel="noopener">Prometheus</a>: Banco de séries temporais e alertas para métricas.</li>
<li><a href="https://github.com/python/cpython"target="_blank" rel="noopener">Python</a>: A linguagem em que a maior parte deste stack é escrita e operada.</li>
<li><a href="https://github.com/pytorch/pytorch"target="_blank" rel="noopener">PyTorch</a>: Framework de deep learning com grafos dinâmicos.</li>
<li><a href="https://github.com/qdrant/qdrant"target="_blank" rel="noopener">Qdrant</a>: Engine de busca vetorial com filtros, escrita em Rust.</li>
<li><a href="https://github.com/r-devel/r-svn"target="_blank" rel="noopener">R</a>: Linguagem e ambiente para estatística.</li>
<li><a href="https://github.com/ray-project/ray"target="_blank" rel="noopener">Ray</a>: Runtime distribuído em Python para treino, tuning e serving.</li>
<li><a href="https://github.com/redpanda-data/redpanda"target="_blank" rel="noopener">Redpanda</a>: Broker compatível com Kafka sem JVM e sem ZooKeeper.</li>
<li><a href="https://github.com/scikit-learn/scikit-learn"target="_blank" rel="noopener">Scikit-learn</a>: Algoritmos clássicos de machine learning em Python.</li>
<li><a href="https://github.com/scipy/scipy"target="_blank" rel="noopener">SciPy</a>: Computação científica construída sobre o NumPy.</li>
<li><a href="https://github.com/SeldonIO/seldon-core"target="_blank" rel="noopener">Seldon</a>: Faz deploy e monitora modelos no Kubernetes.</li>
<li><a href="https://github.com/sodadata/soda-core"target="_blank" rel="noopener">Soda</a>: Checagens de qualidade de dados escritas como regras legíveis.</li>
<li><a href="https://github.com/SQLMesh/sqlmesh"target="_blank" rel="noopener">SQLMesh</a>: Transformações com linhagem por coluna e ambientes virtuais.</li>
<li><a href="https://github.com/StarRocks/starrocks"target="_blank" rel="noopener">StarRocks</a>: Banco OLAP MPP para consultas abaixo de um segundo sobre o lake.</li>
<li><a href="https://github.com/tensorflow/tensorflow"target="_blank" rel="noopener">TensorFlow</a>: Framework de deep learning com toolchain de produção.</li>
<li><a href="https://github.com/trinodb/trino"target="_blank" rel="noopener">Trino</a>: SQL distribuído que federa consultas entre muitas fontes.</li>
<li><a href="https://github.com/vllm-project/vllm"target="_blank" rel="noopener">vLLM</a>: Serving de alto throughput para grandes modelos de linguagem.</li>
<li><a href="https://github.com/weaviate/weaviate"target="_blank" rel="noopener">Weaviate</a>: Banco vetorial com módulos de embedding embutidos.</li>
<li><a href="https://github.com/dmlc/xgboost"target="_blank" rel="noopener">XGBoost</a>: Biblioteca de gradient boosting, o cavalo de batalha dos dados tabulares.</li>
</ul>
<p>O Kafka Connect vem dentro de apache/kafka, o dbt Tests dentro de dbt-core e o Ray Serve dentro de ray-project/ray, então compartilham os repositórios acima.</p>
<p>Sem repositório público, por serem proprietários ou serviços gerenciados: Power BI, Tableau, Fivetran, Amazon S3, Google Cloud Storage, Azure Data Lake Storage, AWS Glue Catalog, Databricks, Starburst, Snowflake, BigQuery, Redshift, Tecton, Pinecone, Weights &amp; Biases, Neptune, Comet, Vertex AI, SageMaker e Azure ML.</p>
]]></content:encoded><category>data-engineering</category><category>machine-learning</category></item><item><title>Diálogo sobre a Nova Ferramenta</title><link>https://apolzek.github.io/2026/09/01/dialogo-sobre-a-nova-ferramenta/</link><guid isPermaLink="true">https://apolzek.github.io/2026/09/01/dialogo-sobre-a-nova-ferramenta/</guid><pubDate>Tue, 01 Sep 2026 12:00:00 GMT</pubDate><description>&lt;p&gt;&lt;em&gt;Cenário: fim de tarde, uma mesa de bar perto do escritório. Glauco chega com o notebook debaixo do braço e abre antes mesmo de sentar.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GLAUCO:&lt;/strong&gt; Sócrates, achei a ferramenta que resolve tudo. Li a documentação ontem à noite, subi um exemplo em vinte minutos e funcionou de primeira. Vou reescrever o sistema inteiro em cima dela.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SÓCRATES:&lt;/strong&gt; Vinte minutos. Quando você faz test drive de um carro, quanto tempo dura ?&lt;/p&gt;</description><content:encoded><![CDATA[<p><em>Cenário: fim de tarde, uma mesa de bar perto do escritório. Glauco chega com o notebook debaixo do braço e abre antes mesmo de sentar.</em></p>
<p><strong>GLAUCO:</strong> Sócrates, achei a ferramenta que resolve tudo. Li a documentação ontem à noite, subi um exemplo em vinte minutos e funcionou de primeira. Vou reescrever o sistema inteiro em cima dela.</p>
<p><strong>SÓCRATES:</strong> Vinte minutos. Quando você faz test drive de um carro, quanto tempo dura ?</p>
<p><strong>GLAUCO:</strong> Uns quinze minutos. Uma volta no quarteirão com o vendedor do lado.</p>
<p><strong>SÓCRATES:</strong> E você já descobriu algum defeito de carro numa volta no quarteirão ?</p>
<p><strong>GLAUCO:</strong> Nunca. Os defeitos aparecem depois. Na estrada, na chuva, no trânsito parado.</p>
<p><strong>SÓCRATES:</strong> Então nesses vinte minutos você conheceu a ferramenta, ou conheceu o percurso que o autor dela escolheu pra você fazer ?</p>
<p><strong>GLAUCO:</strong> O percurso. Mas gostei do percurso.</p>
<p><strong>SÓCRATES:</strong> Gostar já é alguma coisa. Deixa eu te fazer umas perguntas, então. Só não me responde o que você imagina. Me responde o que você sabe.</p>
<p><strong>GLAUCO:</strong> Pode perguntar.</p>
<p><strong>SÓCRATES:</strong> Quantos registros passaram por ela no seu teste ?</p>
<p><strong>GLAUCO:</strong> Uns mil.</p>
<p><strong>SÓCRATES:</strong> E num dia comum, lá dentro ?</p>
<p><strong>GLAUCO:</strong> Uns dez milhões.</p>
<p><strong>SÓCRATES:</strong> E o que ela faz quando não dá conta ? Segura o que está chegando, descarta, ou cai junto ?</p>
<p><strong>GLAUCO:</strong> Não sei. Nunca cheguei nesse ponto.</p>
<p><strong>SÓCRATES:</strong> Ninguém chega, no teste. E repara: a escala não inventa defeito nenhum. Ela promove os que já estavam ali e ninguém via. Uma operação lenta em mil registros é invisível. Em dez milhões, ela é o sistema inteiro. Você não vai descobrir defeitos novos em produção, vai descobrir o tamanho real dos que já tinha.</p>
<p><strong>SÓCRATES:</strong> Em que linguagem ela foi escrita ?</p>
<p><strong>GLAUCO:</strong> Isso importa ? Ela roda num contêiner, eu nem preciso saber.</p>
<p><strong>SÓCRATES:</strong> É a única parte dela que você não vai poder trocar depois. A linguagem já decidiu, antes de você chegar, um monte de coisa que você só vai sentir em produção. Qual é ?</p>
<p><strong>GLAUCO:</strong> Rust.</p>
<p><strong>SÓCRATES:</strong> Então ela é compilada, e isso te dá coisas boas de graça. Vai chegar como um arquivo só, sem interpretador nem dependência solta pra você carregar junto, e sobe num piscar. Ferramenta interpretada te obriga a cuidar dela e do interpretador dela pelo resto da vida.</p>
<p><strong>GLAUCO:</strong> Ótimo. Então é a escolha certa.</p>
<p><strong>SÓCRATES:</strong> É uma escolha, e toda escolha tem o outro lado virado pra baixo. Aquele arquivo único carrega dentro dele as bibliotecas que o autor escolheu, nas versões que ele escolheu, no dia em que ele compilou. Quando aparecer um problema numa dessas bibliotecas, você não troca a peça. Você espera ele compilar de novo.</p>
<p><strong>GLAUCO:</strong> Não tinha pensado nisso.</p>
<p><strong>SÓCRATES:</strong> É o mesmo pacote, olhado do outro lado. Segunda coisa: Rust não tem coletor de lixo(<em>Garbage Collector</em>).</p>
<p><strong>GLAUCO:</strong> Isso é bom, não é ?</p>
<p><strong>SÓCRATES:</strong> É bom pra uma coisa específica. Linguagem com coletor tem um faxineiro que limpa a casa na hora que ele escolhe, não na hora que você pede, e isso aparece como uma lentidão que chega sem avisar e vai embora sozinha, justo quando o volume cresce. Sem coletor, o tempo de resposta é previsível, e previsível vale muito.</p>
<p><strong>GLAUCO:</strong> Então não tenho com que me preocupar de memória.</p>
<p><strong>SÓCRATES:</strong> Tem, e de um tipo mais silencioso. Sem coletor nada é varrido por acaso: o que a ferramenta segurar, ela segura até o fim. Uma fila sem limite ou um cache que ninguém mandou parar não aparecem como pausa, aparecem como uma linha subindo devagar por uma semana até alguém ser acordado de madrugada. Coletor de lixo é barulhento e avisa. A falta dele é silenciosa.</p>
<p><strong>GLAUCO:</strong> Dá pra saber isso antes ?</p>
<p><strong>SÓCRATES:</strong> Duas perguntas resolvem. Quanta memória ela consome depois de uma semana ligada, e não nos dez primeiros minutos. E quantos núcleos ela usa de verdade, porque uma coisa é a linguagem saber usar oito, outra é a ferramenta ter sido escrita pra isso.</p>
<p><strong>GLAUCO:</strong> Isso muda o desenho inteiro.</p>
<p><strong>SÓCRATES:</strong> Muda, e é bem melhor mudar agora, enquanto ainda é papel. Próxima: como uma pessoa prova pra ela que é quem diz ser ?</p>
<p><strong>GLAUCO:</strong> Usuário e senha, criados ali dentro mesmo.</p>
<p><strong>SÓCRATES:</strong> E na sua empresa, como as pessoas provam isso em todo o resto ?</p>
<p><strong>GLAUCO:</strong> Pelo LDAP.</p>
<p><strong>SÓCRATES:</strong> Então ela vai ser o único lugar da empresa onde uma pessoa existe duas vezes, e quem for desligado numa sexta vai sair de todos os lugares menos de um. Agora a outra metade: depois que a pessoa entra, quem decide o que ela pode fazer ?</p>
<p><strong>GLAUCO:</strong> Quem entra pode tudo, eu acho.</p>
<p><strong>SÓCRATES:</strong> Você acabou de tratar como uma coisa só o que são duas. Uma é quem entra. Outra é o que cada um faz depois de entrar. Ferramenta de uma pessoa não precisa separar as duas, porque a pessoa é você.</p>
<p><strong>GLAUCO:</strong> É que ninguém no meu time ia fazer estrago de propósito.</p>
<p><strong>SÓCRATES:</strong> Quase nunca é de propósito. Permissão não existe por desconfiança, existe pra limitar o tamanho do erro de uma pessoa distraída numa terça-feira. Quando todo mundo pode tudo, todo mundo carrega o poder de derrubar tudo, e ninguém pediu esse poder.</p>
<p><strong>GLAUCO:</strong> Nunca tinha pensado assim.</p>
<p><strong>SÓCRATES:</strong> E quando alguém apagar o que não devia, você descobre quem foi ?</p>
<p><strong>GLAUCO:</strong> Não sei se ela guarda isso.</p>
<p><strong>SÓCRATES:</strong> Pergunta. Registro não existe pra acusar ninguém, existe justamente pra que ninguém precise ser acusado. Sem ele, no dia do problema todo mundo que tinha acesso é suspeito, e a conversa vira memória contra memória. Memória é a testemunha mais interessada que existe.</p>
<p><strong>SÓCRATES:</strong> Quando saiu a última versão dela ?</p>
<p><strong>GLAUCO:</strong> Faz uns dois meses, acho.</p>
<p><strong>SÓCRATES:</strong> E antes dessa ?</p>
<p><strong>GLAUCO:</strong> Não olhei.</p>
<p><strong>SÓCRATES:</strong> Olha, porque o intervalo entre as versões é o relógio no qual você vai amarrar o seu. E olha uma coisa mais fina: entre uma versão grande e outra, saem versões pequenas, só com correção ?</p>
<p><strong>GLAUCO:</strong> Não reparei.</p>
<p><strong>SÓCRATES:</strong> Repara. Se saírem, no dia de um problema sério você atualiza uma casa decimal e volta a dormir. Se não saírem, o conserto só chega junto da próxima versão grande, com tudo que ela muda de quebra, e um problema de uma tarde vira uma migração inteira num dia que você não escolheu.</p>
<p><strong>GLAUCO:</strong> Isso eu consigo verificar hoje.</p>
<p><strong>SÓCRATES:</strong> Verifica junto com esta: quantas pessoas de fato mantêm isso ?</p>
<p><strong>GLAUCO:</strong> O repositório tem oito mil estrelas e centenas de contribuidores.</p>
<p><strong>SÓCRATES:</strong> Estrela é aplauso, e aplauso não conserta nada. Contribuidor, na maioria dessas listas, é qualquer um que já corrigiu uma vírgula na documentação. Conta outra coisa: nos últimos seis meses, quantos nomes diferentes mexeram no <em>core</em> da ferramenta ? E desses, quantos sobram depois que você tira o primeiro ?</p>
<p><strong>GLAUCO:</strong> Não sei. Imagino que poucos.</p>
<p><strong>SÓCRATES:</strong> Se sobrar um, você não escolheu uma ferramenta. Escolheu uma pessoa, e essa pessoa não assinou nada com você.</p>
<p><strong>GLAUCO:</strong> Falta muita pergunta ?</p>
<p><strong>SÓCRATES:</strong> Uma. Você disse que ela faz quase tudo que você precisa. E o quase ?</p>
<p><strong>GLAUCO:</strong> O que faltar eu abro uma issue e peço.</p>
<p><strong>SÓCRATES:</strong> Sempre ? Toda vez que aparecer uma necessidade nova, o caminho vai ser pedir e esperar ?</p>
<p><strong>GLAUCO:</strong> É o que dá pra fazer.</p>
<p><strong>SÓCRATES:</strong> Então repara no que você aceitou: o ritmo do que você entrega passa a ser decidido por gente que não conhece o seu problema e não deve nada a você. Existe outro caminho, e ele é a diferença entre uma ferramenta e uma parede: um lugar onde você mesmo escreve o que faltou. Um plugin, um encaixe, um script que ela chama na hora certa. Quando esse lugar existe, o que falta hoje é trabalho seu. Quando não existe, o que falta hoje é espera.</p>
<p><strong>GLAUCO:</strong> Sócrates, eu não soube responder quase nada.</p>
<p><strong>SÓCRATES:</strong> E é a melhor coisa que te aconteceu hoje. Repara que você não descobriu que a ferramenta é ruim. Descobriu que você não conhece ela. São coisas muito diferentes, e só uma das duas tem conserto.</p>
<p><strong>GLAUCO:</strong> Mas eu estava seguro.</p>
<p><strong>SÓCRATES:</strong> Estava seguro do que viu, e você viu vinte minutos escolhidos por outra pessoa. Todo mundo está seguro no começo. A diferença é que uns descobrem as perguntas antes de instalar, e outros descobrem depois, com o sistema inteiro em pé em cima delas.</p>
<p><strong>GLAUCO:</strong> Então eu desisto dela ?</p>
<p><strong>SÓCRATES:</strong> Não foi isso que eu disse. Talvez ela seja ótima. Vai atrás de cada resposta e volta com elas. Se forem boas, você instala sabendo o que instalou. Se forem ruins, você já sabe onde vai doer, e às vezes se instala mesmo assim, de propósito, porque doer naquele lugar é aceitável.</p>
<p><strong>GLAUCO:</strong> E se eu errar mesmo tendo perguntado tudo ?</p>
<p><strong>SÓCRATES:</strong> Vai errar de vez em quando, e tudo bem. Põe o novo onde o erro sai barato, numa borda que você desliga numa tarde sem acordar ninguém, e deixa o meio chato e previsível. <strong>Antes de perguntar o que uma ferramenta faz, pergunta quanto custa sair dela</strong>. Essa é a única resposta que ninguém escreve na documentação.</p>
<p><strong>GLAUCO:</strong> Vou refazer o diagrama.</p>
<p><strong>SÓCRATES:</strong> Hoje não. Guarda uma semana e abre de novo. Entusiasmo é bom combustível e péssimo motorista.</p>
<p>..então Sócrates fechou o notebook, Glauco salvou o diagrama sem apagar nada, e os dois foram tomar uma cerveja.</p>
<h2>Nota<span class="hx:absolute hx:-mt-20" id="nota"></span>
    <a href="#nota" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A ideia foi pegar o modelo de diálogo de <strong>A República, de Platão</strong>, e aplicar num caso de tecnologia. Só a forma é emprestada: perguntas curtas, uma ideia de cada vez, a resposta do interlocutor abrindo a pergunta seguinte.</p>
<p>O caso é familiar pra quem trabalha com isso. Toda semana aparece uma ferramenta nova, open source, com interface bonita, exemplo que sobe em cinco minutos e um gráfico de estrelas subindo. Nesse ponto parece tudo resolvido, e o entusiasmo é legítimo.</p>
<p>As coisas ficam mais complicadas quando as perguntas começam. Como ela se comporta em escala. O que acontece no dia em que você precisa de algo que o autor não previu. Quanto tempo um bug report leva pra virar correção, e se alguém sequer responde. Quantos mantenedores de verdade existem por trás dos números do repositório, e de quantas empresas diferentes eles são. Quem decide o rumo do projeto, e em que lugar essa decisão é discutida. Como ela faz autenticação, e se isso conversa com o login que o resto da empresa já usa. O que ela faz quando um pedaço dela cai. Se dá pra auditar depois quem fez o quê lá dentro. E quanto trabalho é, de fato, integrá-la ao que você já tem de pé.</p>
<p>Quase nenhuma dessas respostas está na primeira página. A maioria aparece uns seis meses depois, quando trocar já não sai de graça.</p>
]]></content:encoded><category>filosofia</category><category>dialogos</category><category>tecnologia</category></item><item><title>Checklist de repositório</title><link>https://apolzek.github.io/2026/09/01/repository-checklist/</link><guid isPermaLink="true">https://apolzek.github.io/2026/09/01/repository-checklist/</guid><pubDate>Tue, 01 Sep 2026 12:00:00 GMT</pubDate><description>&lt;p&gt;Um checklist do que um repositório bem mantido deveria entregar, na sua
documentação, no seu código e na sua automação.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Como usar:&lt;/strong&gt; nem todo repo precisa de todos os itens. Os itens marcados com
&lt;code&gt;(A)&lt;/code&gt; são &lt;em&gt;avançados&lt;/em&gt;, valem para projetos OSS, monorepos ou qualquer coisa
com contribuidores externos. Tudo que não está marcado é o mínimo.&lt;/p&gt;
&lt;p&gt;As seções marcadas como &lt;em&gt;condicionais&lt;/em&gt; só se aplicam quando o repo é aquele tipo
de projeto (um serviço HTTP, um app em contêiner, um workload Kubernetes).&lt;/p&gt;</description><content:encoded><![CDATA[<p>Um checklist do que um repositório bem mantido deveria entregar, na sua
documentação, no seu código e na sua automação.</p>
<p><strong>Como usar:</strong> nem todo repo precisa de todos os itens. Os itens marcados com
<code>(A)</code> são <em>avançados</em>, valem para projetos OSS, monorepos ou qualquer coisa
com contribuidores externos. Tudo que não está marcado é o mínimo.</p>
<p>As seções marcadas como <em>condicionais</em> só se aplicam quando o repo é aquele tipo
de projeto (um serviço HTTP, um app em contêiner, um workload Kubernetes).</p>
<h2>Índice<span class="hx:absolute hx:-mt-20" id="índice"></span>
    <a href="#%c3%adndice" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li><a href="#1-documenta%c3%a7%c3%a3o">1. Documentação</a>
<ul>
<li><a href="#11-readmemd">1.1 README.md</a></li>
<li><a href="#12-arquivos-de-governan%c3%a7a-e-pol%c3%adtica">1.2 Arquivos de governança e política</a></li>
<li><a href="#13-docs-mais-profundas-docs">1.3 Docs mais profundas (<code>docs/</code>)</a></li>
<li><a href="#14-changelog-e-releases">1.4 Changelog e releases</a></li>
</ul>
</li>
<li><a href="#2-c%c3%b3digo">2. Código</a>
<ul>
<li><a href="#21-layout-e-conven%c3%a7%c3%b5es">2.1 Layout e convenções</a></li>
<li><a href="#22-configura%c3%a7%c3%a3o">2.2 Configuração</a></li>
<li><a href="#23-fonte-%c3%banica-de-verdade--c%c3%b3digo-gerado-a">2.3 Fonte única de verdade / código gerado (A)</a></li>
</ul>
</li>
<li><a href="#3-testes">3. Testes</a>
<ul>
<li><a href="#31-testes-unit%c3%a1rios">3.1 Testes unitários</a></li>
<li><a href="#32-integra%c3%a7%c3%a3o-e-end-to-end">3.2 Integração e end-to-end</a></li>
<li><a href="#33-performance-e-robustez-a">3.3 Performance e robustez (A)</a></li>
<li><a href="#34-cobertura">3.4 Cobertura</a></li>
</ul>
</li>
<li><a href="#4-artefatos-de-api-condicional-servi%c3%a7os-httpgrpc">4. Artefatos de API (condicional: serviços HTTP/gRPC)</a></li>
<li><a href="#5-build-e-runtime-condicional-apps-em-cont%c3%aainer">5. Build e runtime (condicional: apps em contêiner)</a>
<ul>
<li><a href="#51-docker">5.1 Docker</a></li>
<li><a href="#52-docker-compose">5.2 Docker Compose</a></li>
<li><a href="#53-kubernetes-condicional">5.3 Kubernetes (condicional)</a></li>
</ul>
</li>
<li><a href="#6-cicd">6. CI/CD</a>
<ul>
<li><a href="#61-checagens-b%c3%a1sicas">6.1 Checagens básicas</a></li>
<li><a href="#62-seguran%c3%a7a">6.2 Segurança</a></li>
<li><a href="#63-depend%c3%aancias">6.3 Dependências</a></li>
<li><a href="#64-automa%c3%a7%c3%a3o-do-reposit%c3%b3rio-a">6.4 Automação do repositório (A)</a></li>
</ul>
</li>
<li><a href="#7-metadados-do-reposit%c3%b3rio">7. Metadados do repositório</a></li>
<li><a href="#conjunto-m%c3%adnimo-vi%c3%a1vel">Conjunto mínimo viável</a></li>
</ul>
<h2>1. Documentação<span class="hx:absolute hx:-mt-20" id="1-documentação"></span>
    <a href="#1-documenta%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>1.1 README.md<span class="hx:absolute hx:-mt-20" id="11-readmemd"></span>
    <a href="#11-readmemd" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> Visão geral do projeto: o que ele faz e, mais importante, <em>por que</em> existe</li>
<li><input disabled="" type="checkbox"> Badges de status: build do CI, cobertura de testes, última release, nível de estabilidade</li>
<li><input disabled="" type="checkbox"> Nível de estabilidade / maturidade (<code>development</code> → <code>alpha</code> → <code>beta</code> → <code>stable</code> → <code>deprecated</code>)</li>
<li><input disabled="" type="checkbox"> Política de suporte: quem mantém, onde perguntar, tempo de resposta esperado</li>
<li><input disabled="" type="checkbox"> Instruções de instalação</li>
<li><input disabled="" type="checkbox"> Como rodar o app localmente</li>
<li><input disabled="" type="checkbox"> Como configurar o ambiente (<code>.env.example</code> commitado, <code>.env</code> no git-ignore)</li>
<li><input disabled="" type="checkbox"> Exemplos de uso (copiáveis e realmente testados)</li>
<li><input disabled="" type="checkbox"> Diagrama de arquitetura ou uma seção curta de &ldquo;como as peças se encaixam&rdquo;</li>
<li><input disabled="" type="checkbox"> Link para o guia de contribuição</li>
<li><input disabled="" type="checkbox"> Informação de licença</li>
<li><input disabled="" type="checkbox"> Um README por módulo/subpacote, não só na raiz (A)</li>
<li><input disabled="" type="checkbox"> Seções de status geradas automaticamente, delimitadas por marcadores, para nunca ficarem defasadas (A)</li>
</ul>
<h3>1.2 Arquivos de governança e política<span class="hx:absolute hx:-mt-20" id="12-arquivos-de-governança-e-política"></span>
    <a href="#12-arquivos-de-governan%c3%a7a-e-pol%c3%adtica" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> <code>LICENSE</code> (+ <code>NOTICE</code> se você redistribui código de terceiros)</li>
<li><input disabled="" type="checkbox"> <code>CONTRIBUTING.md</code>: setup local, como rodar os testes, regras de título e
descrição de PR, expectativas de review</li>
<li><input disabled="" type="checkbox"> <code>CODEOWNERS</code>: ownership por diretório, dispara pedidos de review automáticos</li>
<li><input disabled="" type="checkbox"> <code>SECURITY.md</code>: como reportar uma vulnerabilidade, em privado</li>
<li><input disabled="" type="checkbox"> <code>CODE_OF_CONDUCT.md</code></li>
<li><input disabled="" type="checkbox"> <code>AGENTS.md</code> / <code>CLAUDE.md</code>: política de contribuição assistida por IA (trailer
de divulgação, o que agentes podem e não podem fazer)</li>
<li><input disabled="" type="checkbox"> <code>.github/pull_request_template.md</code></li>
<li><input disabled="" type="checkbox"> <code>.github/ISSUE_TEMPLATE/</code>: bug report, feature request, mais um <code>config.yml</code>
apontando para os canais de suporte</li>
<li><input disabled="" type="checkbox"> Política de triagem de issues: labels, prioridades, política de stale (A)</li>
<li><input disabled="" type="checkbox"> Papéis de mantenedor/aprovador e como chegar lá (A)</li>
</ul>
<h3>1.3 Docs mais profundas (<code>docs/</code>)<span class="hx:absolute hx:-mt-20" id="13-docs-mais-profundas-docs"></span>
    <a href="#13-docs-mais-profundas-docs" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> Processo de release, escrito e repetível</li>
<li><input disabled="" type="checkbox"> Guia de testes: como rodar cada camada de teste localmente</li>
<li><input disabled="" type="checkbox"> Guia de onboarding para novos módulos/componentes (A)</li>
<li><input disabled="" type="checkbox"> ADRs (Architecture Decision Records): o <em>porquê</em> por trás do design</li>
<li><input disabled="" type="checkbox"> Runbook / docs operacionais: alertas, dashboards, falhas comuns</li>
<li><input disabled="" type="checkbox"> Docs de referência geradas: cada opção de config, default e métrica emitida,
produzidas a partir da fonte de verdade em vez de escritas à mão (A)</li>
</ul>
<h3>1.4 Changelog e releases<span class="hx:absolute hx:-mt-20" id="14-changelog-e-releases"></span>
    <a href="#14-changelog-e-releases" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> <code>CHANGELOG.md</code> seguindo SemVer / Keep a Changelog</li>
<li><input disabled="" type="checkbox"> Fragmentos de changelog: um arquivo pequeno por PR, montados na hora da release.
Mata conflitos de merge e obriga o autor a declarar o impacto para o usuário (A)</li>
<li><input disabled="" type="checkbox"> Changelog separado para mudanças de API/breaking vs. mudanças visíveis ao usuário (A)</li>
<li><input disabled="" type="checkbox"> Releases com tag e release notes geradas a partir do changelog</li>
<li><input disabled="" type="checkbox"> Política de versionamento e de deprecação documentada</li>
</ul>
<h2>2. Código<span class="hx:absolute hx:-mt-20" id="2-código"></span>
    <a href="#2-c%c3%b3digo" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>2.1 Layout e convenções<span class="hx:absolute hx:-mt-20" id="21-layout-e-convenções"></span>
    <a href="#21-layout-e-conven%c3%a7%c3%b5es" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> Layout de projeto consistente e documentado</li>
<li><input disabled="" type="checkbox"> Cabeçalho de licença/SPDX em todo arquivo-fonte, cobrado pelo linter</li>
<li><input disabled="" type="checkbox"> Comentário de documentação por pacote (<code>doc.go</code> ou equivalente)</li>
<li><input disabled="" type="checkbox"> Config do linter commitada, com conjunto explícito de linters e exceções
justificadas por caminho, não os defaults da ferramenta</li>
<li><input disabled="" type="checkbox"> Formatador cobrado no CI, não deixado para o revisor</li>
<li><input disabled="" type="checkbox"> Restrições de import cobradas (nenhum import proibido/interno vazando) (A)</li>
<li><input disabled="" type="checkbox"> Um task runner (Makefile / justfile / npm scripts) com alvos padrão:
<code>test</code>, <code>lint</code>, <code>fmt</code>, <code>build</code>, <code>run</code>, <code>integration-test</code>, <code>tidy</code></li>
<li><input disabled="" type="checkbox"> Os mesmos comandos funcionam na raiz e dentro de cada módulo (A)</li>
</ul>
<h3>2.2 Configuração<span class="hx:absolute hx:-mt-20" id="22-configuração"></span>
    <a href="#22-configura%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> Struct de config tipada com <code>Validate()</code> explícito e mensagens de erro claras</li>
<li><input disabled="" type="checkbox"> Falhar rápido com config inválida na inicialização, nunca no meio de um request</li>
<li><input disabled="" type="checkbox"> Config de exemplo commitada, carregada por um teste de verdade</li>
<li><input disabled="" type="checkbox"> Schema de config publicado (JSON Schema / equivalente) para autocomplete no
editor e validação no CI (A)</li>
<li><input disabled="" type="checkbox"> <code>examples/</code> com configurações completas baseadas em cenário: não trechos</li>
<li><input disabled="" type="checkbox"> Segredos nunca em arquivos de config nem no repo; injetados em runtime</li>
<li><input disabled="" type="checkbox"> Logging estruturado, métricas e tracing plugados e documentados</li>
</ul>
<h3>2.3 Fonte única de verdade / código gerado (A)<span class="hx:absolute hx:-mt-20" id="23-fonte-única-de-verdade--código-gerado-a"></span>
    <a href="#23-fonte-%c3%banica-de-verdade--c%c3%b3digo-gerado-a" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> Arquivo de metadados declarativo por componente (owners, estabilidade, config,
telemetria emitida)</li>
<li><input disabled="" type="checkbox"> Código, docs e seções do README gerados a partir dele</li>
<li><input disabled="" type="checkbox"> O CI verifica que a saída gerada está atualizada: defasagem vira build
quebrado em vez de documentação velha</li>
</ul>
<h2>3. Testes<span class="hx:absolute hx:-mt-20" id="3-testes"></span>
    <a href="#3-testes" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>3.1 Testes unitários<span class="hx:absolute hx:-mt-20" id="31-testes-unitários"></span>
    <a href="#31-testes-unit%c3%a1rios" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> Testes unitários que realmente afirmam comportamento, não só executam linhas</li>
<li><input disabled="" type="checkbox"> Testes table-driven para lógica cheia de ramificações</li>
<li><input disabled="" type="checkbox"> Determinísticos: sem sleeps, sem relógio real, sem rede</li>
<li><input disabled="" type="checkbox"> Fixtures de teste (<code>testdata/</code>) versionadas, com um comando para regenerá-las</li>
</ul>
<h3>3.2 Integração e end-to-end<span class="hx:absolute hx:-mt-20" id="32-integração-e-end-to-end"></span>
    <a href="#32-integra%c3%a7%c3%a3o-e-end-to-end" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> Testes de integração isolados atrás de uma build tag / comando separado para
não atrasarem a execução unitária</li>
<li><input disabled="" type="checkbox"> Dependências reais via contêineres efêmeros (Testcontainers ou equivalente)</li>
<li><input disabled="" type="checkbox"> Testes end-to-end contra um deploy realista (A)</li>
<li><input disabled="" type="checkbox"> Testes de contrato para APIs consumidas e fornecidas (A)</li>
</ul>
<h3>3.3 Performance e robustez (A)<span class="hx:absolute hx:-mt-20" id="33-performance-e-robustez-a"></span>
    <a href="#33-performance-e-robustez-a" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> Benchmarks para os caminhos quentes, com um jeito documentado de comparar execuções</li>
<li><input disabled="" type="checkbox"> Testes de carga com uma baseline de performance declarada</li>
<li><input disabled="" type="checkbox"> Testes de fuzz para tudo que faz parse de entrada externa</li>
<li><input disabled="" type="checkbox"> Detecção de vazamento: goroutines, conexões, file descriptors</li>
<li><input disabled="" type="checkbox"> Race detector habilitado no CI</li>
</ul>
<h3>3.4 Cobertura<span class="hx:absolute hx:-mt-20" id="34-cobertura"></span>
    <a href="#34-cobertura" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> Relatório de cobertura gerado no CI e publicado</li>
<li><input disabled="" type="checkbox"> Limite de cobertura commitado no repo, dividido em alvos de <em>projeto</em> e de
<em>patch</em> (ex.: 85% no total, 95% no diff)</li>
<li><input disabled="" type="checkbox"> Cobertura por módulo/componente, para um repo grande não esconder um canto
ruim atrás de uma média boa (A)</li>
</ul>
<h2>4. Artefatos de API <em>(condicional: serviços HTTP/gRPC)</em><span class="hx:absolute hx:-mt-20" id="4-artefatos-de-api-condicional-serviços-httpgrpc"></span>
    <a href="#4-artefatos-de-api-condicional-servi%c3%a7os-httpgrpc" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li><input disabled="" type="checkbox"> Spec OpenAPI / Swagger, versionada no repo</li>
<li><input disabled="" type="checkbox"> Spec gerada a partir do código ou validada contra ele, para não ficar defasada</li>
<li><input disabled="" type="checkbox"> Coleção do Postman / Insomnia / Bruno</li>
<li><input disabled="" type="checkbox"> Arquivos de request <code>.http</code> (executáveis a partir do editor)</li>
<li><input disabled="" type="checkbox"> <code>curl.txt</code> com exemplos copiáveis</li>
<li><input disabled="" type="checkbox"> Fluxo de autenticação documentado</li>
<li><input disabled="" type="checkbox"> Catálogo de erros: códigos, significados e como se recuperar</li>
<li><input disabled="" type="checkbox"> Versionamento e caminho de deprecação documentados para os endpoints</li>
</ul>
<h2>5. Build e runtime <em>(condicional: apps em contêiner)</em><span class="hx:absolute hx:-mt-20" id="5-build-e-runtime-condicional-apps-em-contêiner"></span>
    <a href="#5-build-e-runtime-condicional-apps-em-cont%c3%aainer" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>5.1 Docker<span class="hx:absolute hx:-mt-20" id="51-docker"></span>
    <a href="#51-docker" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> <code>Dockerfile</code> com build multi-stage</li>
<li><input disabled="" type="checkbox"> Imagem base mínima e pinada (distroless/slim), usuário non-root</li>
<li><input disabled="" type="checkbox"> <code>.dockerignore</code></li>
<li><input disabled="" type="checkbox"> Healthcheck definido</li>
<li><input disabled="" type="checkbox"> Builds reproduzíveis: dependências pinadas, nada de <code>latest</code></li>
<li><input disabled="" type="checkbox"> Scan de vulnerabilidade da imagem no CI (A)</li>
<li><input disabled="" type="checkbox"> SBOM gerado e publicado junto com a imagem (A)</li>
</ul>
<h3>5.2 Docker Compose<span class="hx:absolute hx:-mt-20" id="52-docker-compose"></span>
    <a href="#52-docker-compose" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> <code>docker-compose.yml</code> que sobe o stack inteiro com um comando</li>
<li><input disabled="" type="checkbox"> Overrides por ambiente (<code>docker-compose.dev.yml</code>, <code>.prod.yml</code>)</li>
<li><input disabled="" type="checkbox"> Dependências (banco, broker, cache) incluídas, para um dev novo não precisar de mais nada</li>
</ul>
<h3>5.3 Kubernetes <em>(condicional)</em><span class="hx:absolute hx:-mt-20" id="53-kubernetes-condicional"></span>
    <a href="#53-kubernetes-condicional" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> Manifests, ou um Helm chart com <code>values.yaml</code> documentado</li>
<li><input disabled="" type="checkbox"> Requests e limits de recursos definidos</li>
<li><input disabled="" type="checkbox"> Probes de liveness / readiness / startup</li>
<li><input disabled="" type="checkbox"> Gestão de segredos (External Secrets, SOPS, sealed-secrets, nunca em texto puro)</li>
<li><input disabled="" type="checkbox"> Configuração de Ingress</li>
<li><input disabled="" type="checkbox"> Overlays por ambiente (Kustomize) ou arquivos de values</li>
<li><input disabled="" type="checkbox"> <code>NetworkPolicy</code>, <code>PodDisruptionBudget</code>, security context (A)</li>
<li><input disabled="" type="checkbox"> Manifests lintados e validados no CI (<code>kubeconform</code>, <code>helm lint</code>) (A)</li>
</ul>
<h2>6. CI/CD<span class="hx:absolute hx:-mt-20" id="6-cicd"></span>
    <a href="#6-cicd" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>6.1 Checagens básicas<span class="hx:absolute hx:-mt-20" id="61-checagens-básicas"></span>
    <a href="#61-checagens-b%c3%a1sicas" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> Lint</li>
<li><input disabled="" type="checkbox"> Checagem de formatação</li>
<li><input disabled="" type="checkbox"> Testes unitários</li>
<li><input disabled="" type="checkbox"> Relatório de cobertura + cobrança do limite</li>
<li><input disabled="" type="checkbox"> Build do artefato (binário e/ou imagem)</li>
<li><input disabled="" type="checkbox"> Testes de integração, num job separado</li>
<li><input disabled="" type="checkbox"> Status checks obrigatórios configurados na branch protegida</li>
<li><input disabled="" type="checkbox"> Matriz de build entre os SOs/versões suportados (A)</li>
<li><input disabled="" type="checkbox"> Detecção de escopo: rodar só o que o diff realmente afeta (A)</li>
<li><input disabled="" type="checkbox"> Sharding de jobs para manter o tempo de parede são em repos grandes (A)</li>
</ul>
<h3>6.2 Segurança<span class="hx:absolute hx:-mt-20" id="62-segurança"></span>
    <a href="#62-seguran%c3%a7a" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> Scan de vulnerabilidades em dependências</li>
<li><input disabled="" type="checkbox"> SAST (CodeQL ou equivalente)</li>
<li><input disabled="" type="checkbox"> Secret scanning habilitado</li>
<li><input disabled="" type="checkbox"> Checagem de conformidade de licença nas dependências</li>
<li><input disabled="" type="checkbox"> Os próprios arquivos de workflow lintados (<code>actionlint</code>, <code>zizmor</code>) (A)</li>
<li><input disabled="" type="checkbox"> Scripts shell lintados (<code>shellcheck</code>) (A)</li>
<li><input disabled="" type="checkbox"> <code>permissions:</code> mínimas por workflow; actions de terceiros pinadas por SHA</li>
<li><input disabled="" type="checkbox"> OpenSSF Scorecard (A)</li>
</ul>
<h3>6.3 Dependências<span class="hx:absolute hx:-mt-20" id="63-dependências"></span>
    <a href="#63-depend%c3%aancias" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> Atualizações automáticas (Renovate / Dependabot) com estratégia de agrupamento</li>
<li><input disabled="" type="checkbox"> Lockfile commitado</li>
<li><input disabled="" type="checkbox"> Checagem automática de tidy/prune para o manifesto não ficar defasado</li>
<li><input disabled="" type="checkbox"> Checagem de compatibilidade da API pública entre versões (A)</li>
</ul>
<h3>6.4 Automação do repositório (A)<span class="hx:absolute hx:-mt-20" id="64-automação-do-repositório-a"></span>
    <a href="#64-automa%c3%a7%c3%a3o-do-reposit%c3%b3rio-a" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><input disabled="" type="checkbox"> Atribuição automática de revisores a partir do <code>CODEOWNERS</code></li>
<li><input disabled="" type="checkbox"> Labels automáticas em PRs e issues por área afetada</li>
<li><input disabled="" type="checkbox"> Avisar os owners em issues novas que tocam a área deles</li>
<li><input disabled="" type="checkbox"> Tratamento de issues/PRs stale</li>
<li><input disabled="" type="checkbox"> Boas-vindas a contribuidores de primeira viagem</li>
<li><input disabled="" type="checkbox"> Congelamento de merge durante a janela de release</li>
<li><input disabled="" type="checkbox"> Relatório periódico de saúde do repo (issues abertas, áreas sem dono)</li>
<li><input disabled="" type="checkbox"> Preparação de release automatizada (bump de versão, montagem do changelog, tag)</li>
</ul>
<h2>7. Metadados do repositório<span class="hx:absolute hx:-mt-20" id="7-metadados-do-repositório"></span>
    <a href="#7-metadados-do-reposit%c3%b3rio" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li><input disabled="" type="checkbox"> Topics/tags com as tecnologias usadas, para ser encontrável</li>
<li><input disabled="" type="checkbox"> Descrição do repositório clara, de uma linha</li>
<li><input disabled="" type="checkbox"> Regras de proteção na branch padrão</li>
<li><input disabled="" type="checkbox"> Commits assinados ou tags assinadas (A)</li>
<li><input disabled="" type="checkbox"> Aviso de arquivamento/deprecação quando um repo deixa de ser mantido</li>
</ul>
<h2>Conjunto mínimo viável<span class="hx:absolute hx:-mt-20" id="conjunto-mínimo-viável"></span>
    <a href="#conjunto-m%c3%adnimo-vi%c3%a1vel" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se você só fizer dez coisas, faça estas:</p>
<ol>
<li>README que coloca alguém rodando localmente em menos de cinco minutos</li>
<li><code>LICENSE</code></li>
<li><code>CONTRIBUTING.md</code></li>
<li><code>.env.example</code> commitado, segredos fora do repo</li>
<li>Testes unitários com limite de cobertura cobrado no CI</li>
<li>Lint + formatação cobrados no CI</li>
<li><code>Dockerfile</code> multi-stage e um <code>docker-compose up</code> de um comando só</li>
<li><code>CHANGELOG.md</code> e releases com tag</li>
<li>Scan de dependências e vulnerabilidades</li>
<li>Proteção de branch com checks obrigatórios</li>
</ol>
]]></content:encoded><category>engenharia-de-software</category><category>git</category><category>open-source</category></item><item><title>Apache Flink: Aprendendo em Público, parte 1</title><link>https://apolzek.github.io/2026/08/30/apache-flink-aprendendo-em-publico-parte-1/</link><guid isPermaLink="true">https://apolzek.github.io/2026/08/30/apache-flink-aprendendo-em-publico-parte-1/</guid><pubDate>Sun, 30 Aug 2026 12:00:00 GMT</pubDate><description>&lt;h2&gt;Índice&lt;span class="hx:absolute hx:-mt-20" id="índice"&gt;&lt;/span&gt;
&lt;a href="#%c3%adndice" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#%c3%adndice"&gt;Índice&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#introdu%c3%a7%c3%a3o"&gt;Introdução&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#meu-objetivo"&gt;Meu objetivo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#o-problema-que-o-flink-resolve"&gt;O problema que o Flink resolve&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#casos-de-uso-reais"&gt;Casos de uso reais&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#a-arquitetura-em-poucas-pe%c3%a7as"&gt;A arquitetura, em poucas peças&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#fontes-e-destinos"&gt;Fontes e destinos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#paralelismo"&gt;Paralelismo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#redistribui%c3%a7%c3%a3o-e-data-skew"&gt;Redistribuição e data skew&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#operator-chaining"&gt;Operator chaining&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#rich-functions"&gt;Rich functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tempo-a-parte-que-quebra-a-intui%c3%a7%c3%a3o"&gt;Tempo: a parte que quebra a intuição&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#watermarks"&gt;Watermarks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#janelas"&gt;Janelas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#processfunction"&gt;ProcessFunction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#estado"&gt;Estado&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#checkpoints-e-savepoints"&gt;Checkpoints e savepoints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#flink-sql"&gt;Flink SQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#in-praxi-na-pr%c3%a1tica"&gt;In praxi (&amp;ldquo;na prática&amp;rdquo;)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#deploy"&gt;Deploy&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#modos-de-execu%c3%a7%c3%a3o"&gt;Modos de execução&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#kubernetes"&gt;Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#mem%c3%b3ria"&gt;Memória&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#cpu-e-slots"&gt;CPU e slots&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#disco-local"&gt;Disco local&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#s3-e-o-sistema-de-arquivos-remoto"&gt;S3 e o sistema de arquivos remoto&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#o-ecossistema-em-volta"&gt;O ecossistema em volta&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#complexidades"&gt;Complexidades&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#o-que-ficou"&gt;O que ficou&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Introdução&lt;span class="hx:absolute hx:-mt-20" id="introdução"&gt;&lt;/span&gt;
&lt;a href="#introdu%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Este texto é o resultado do que estudei e experimentei ao longo desta semana. Não é um tutorial de quem domina Apache Flink, nem pretende ser uma referência sobre o assunto. É mais como um caderno de anotações de alguém que está aprendendo, testando algumas coisas na prática e compartilhando o que conseguiu entender e descobrir até aqui.&lt;/p&gt;</description><content:encoded><![CDATA[<h2>Índice<span class="hx:absolute hx:-mt-20" id="índice"></span>
    <a href="#%c3%adndice" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li><a href="#%c3%adndice">Índice</a></li>
<li><a href="#introdu%c3%a7%c3%a3o">Introdução</a></li>
<li><a href="#meu-objetivo">Meu objetivo</a></li>
<li><a href="#o-problema-que-o-flink-resolve">O problema que o Flink resolve</a></li>
<li><a href="#casos-de-uso-reais">Casos de uso reais</a></li>
<li><a href="#a-arquitetura-em-poucas-pe%c3%a7as">A arquitetura, em poucas peças</a></li>
<li><a href="#fontes-e-destinos">Fontes e destinos</a></li>
<li><a href="#paralelismo">Paralelismo</a></li>
<li><a href="#redistribui%c3%a7%c3%a3o-e-data-skew">Redistribuição e data skew</a></li>
<li><a href="#operator-chaining">Operator chaining</a></li>
<li><a href="#rich-functions">Rich functions</a></li>
<li><a href="#tempo-a-parte-que-quebra-a-intui%c3%a7%c3%a3o">Tempo: a parte que quebra a intuição</a></li>
<li><a href="#watermarks">Watermarks</a></li>
<li><a href="#janelas">Janelas</a></li>
<li><a href="#processfunction">ProcessFunction</a></li>
<li><a href="#estado">Estado</a></li>
<li><a href="#checkpoints-e-savepoints">Checkpoints e savepoints</a></li>
<li><a href="#flink-sql">Flink SQL</a></li>
<li><a href="#in-praxi-na-pr%c3%a1tica">In praxi (&ldquo;na prática&rdquo;)</a></li>
<li><a href="#deploy">Deploy</a>
<ul>
<li><a href="#modos-de-execu%c3%a7%c3%a3o">Modos de execução</a></li>
<li><a href="#kubernetes">Kubernetes</a></li>
<li><a href="#mem%c3%b3ria">Memória</a></li>
<li><a href="#cpu-e-slots">CPU e slots</a></li>
<li><a href="#disco-local">Disco local</a></li>
<li><a href="#s3-e-o-sistema-de-arquivos-remoto">S3 e o sistema de arquivos remoto</a></li>
</ul>
</li>
<li><a href="#o-ecossistema-em-volta">O ecossistema em volta</a></li>
<li><a href="#complexidades">Complexidades</a></li>
<li><a href="#o-que-ficou">O que ficou</a></li>
</ul>
<h2>Introdução<span class="hx:absolute hx:-mt-20" id="introdução"></span>
    <a href="#introdu%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Este texto é o resultado do que estudei e experimentei ao longo desta semana. Não é um tutorial de quem domina Apache Flink, nem pretende ser uma referência sobre o assunto. É mais como um caderno de anotações de alguém que está aprendendo, testando algumas coisas na prática e compartilhando o que conseguiu entender e descobrir até aqui.</p>
<p>Sempre achei que explicar é a melhor forma de descobrir o que a gente realmente entendeu. Quando escrevo, os buracos aparecem sozinhos..</p>
<p>O Apache Flink é interessante principalmente porque tratou <strong>streaming</strong> como o caso geral, e não como um remendo sobre processamento em lote. Enquanto concorrentes da época simulavam fluxo contínuo com micro-batches, o Flink processa registro a registro, com latência baixa e throughput alto, mantendo estado distribuído consistente através de snapshots assíncronos (uma variação do algoritmo de <em>Chandy-Lamport</em>) que garantem semântica exactly-once mesmo após falhas. Ele também oferece noção de tempo de evento com <em>watermarks</em>, o que permite lidar corretamente com dados que chegam fora de ordem ou atrasados, algo essencial no mundo real. Sua origem é acadêmica: nasceu em 2010 como Stratosphere, projeto de pesquisa da TU Berlin em parceria com a Universidade Humboldt e o Hasso Plattner Institute, sob liderança de Volker Markl. Em 2014 o código foi doado à Apache Software Foundation, rebatizado de Flink (&ldquo;ágil&rdquo;, em alemão, daí o esquilo do logo) e promovido a projeto de topo ainda em dezembro daquele ano. Os criadores fundaram a <a href="https://github.com/dataArtisans"target="_blank" rel="noopener">data Artisans</a> para comercializá-lo, empresa comprada pelo Alibaba em 2019 e renomeada Ververica. O próprio Alibaba já mantinha um fork interno (Blink) para lidar com o volume do Singles&rsquo; Day, e boa parte dessas melhorias voltou para o projeto principal, ajudando a consolidar o Flink como padrão de fato em processamento de streams em empresas como Netflix, Uber e Stripe.</p>
<h2>Meu objetivo<span class="hx:absolute hx:-mt-20" id="meu-objetivo"></span>
    <a href="#meu-objetivo" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Meu objetivo é usar o Apache Flink conectado ao Kafka para processar dados OTLP do OpenTelemetry (traces, métricas e logs) e extrair mais inteligência dessa telemetria em tempo real, em vez de apenas armazená-la e consultá-la depois. A ideia é usar esse pipeline como laboratório para testar casos como detecção de anomalias em séries temporais de métricas e latências, geração de alertas com contexto e supressão de ruído, análise de causa raiz (RCA) correlacionando spans, logs e métricas de serviços afetados, predição de degradação e saturação de recursos antes que o incidente aconteça, e detecção de dados sensíveis trafegando indevidamente em atributos de span e mensagens de log. Além disso, quero explorar outros cenários em que o Flink se encaixa bem: enriquecimento e sampling inteligente de traces, decidindo com estado quais traces reter com base no que aconteceu na requisição inteira (tail-based sampling) em vez de descartar aleatoriamente na borda; cálculo contínuo de SLIs e error budgets por serviço, com janelas deslizantes e agregações por endpoint, time e versão de deploy; e detecção de mudança de comportamento após releases, comparando o perfil de tráfego, erros e latência entre versões para identificar regressões introduzidas por um deploy específico. Pretendo detalhar tudo isso em breve (:</p>
<h2>O problema que o Flink resolve<span class="hx:absolute hx:-mt-20" id="o-problema-que-o-flink-resolve"></span>
    <a href="#o-problema-que-o-flink-resolve" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Durante muito tempo o mundo de dados foi organizado em torno do lote(<em>batch</em>). Você acumula dados durante o dia, roda um job à meia-noite, e de manhã o relatório está pronto. Isso funciona, e continua funcionando para muita coisa. O problema é quando a resposta precisa vir antes. Detectar uma fraude de cartão dez horas depois da compra é o mesmo que não detectar. Um alerta de infraestrutura que chega no dia seguinte é um post-mortem, não um alerta. Nesses casos, você não quer perguntar aos dados de tempos em tempos, você quer que os dados respondam continuamente. Essa é a inversão que o processamento de streams propõe:</p>
<ul>
<li><strong>Batch</strong>: a consulta é permanente, os dados são finitos. Você roda a query contra um conjunto parado.</li>
<li><strong>Streaming</strong>: os dados são infinitos, a consulta é que fica parada. Ela é registrada uma vez e reage a cada evento que passa.</li>
</ul>
<p>O Apache Flink é um motor de processamento distribuído voltado para a construção desse segundo tipo de aplicação. Sua premissa fundadora: tudo é um stream. Um arquivo CSV de ontem é um stream que tem começo e fim (<em>bounded</em>). O tópico Kafka com cliques do seu site é um stream que nunca acaba (<em>unbounded</em>). Um dos pontos que mais me chamou atenção durante o estudo foi o modelo unificado de processamento: no Flink, batch pode ser entendido como um caso particular de streaming, em que o fluxo de dados possui um início e um fim definidos. Isso significa que o modelo de execução não depende de dois motores completamente distintos, mas de uma mesma arquitetura capaz de processar tanto fluxos contínuos quanto conjuntos de dados finitos.</p>
<h2>Casos de uso reais<span class="hx:absolute hx:-mt-20" id="casos-de-uso-reais"></span>
    <a href="#casos-de-uso-reais" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Vale olhar onde isso aparece na prática, porque a lista ajuda a entender que tipo de problema o Flink resolve bem:</p>
<ul>
<li>Detecção de fraude, em bancos e fintechs. Cada transação é avaliada em milissegundos contra o histórico do cliente, mantido como estado por chave (<code>keyBy</code> no id do cliente). Regras como &ldquo;3 compras em cidades diferentes em 10 minutos&rdquo; são expressas com janelas, timers e a biblioteca de detecção de padrões complexos (CEP).</li>
<li>Pipelines de CDC para data lake. O Flink CDC acompanha o log de transações do banco (o binlog no MySQL, o WAL via replicação lógica no Postgres) e replica cada insert, update e delete em tempo real para formatos de tabela como Apache Iceberg, Paimon ou Hudi. A Stripe é um dos casos públicos de captura de mudanças em escala com Flink.</li>
<li>Analytics e dashboards em tempo real. Agregações por janela (pedidos por minuto, receita por região) alimentando painéis ao vivo. Uber, Netflix e Alibaba operam milhares de jobs Flink com esse perfil.</li>
<li>Aplicações orientadas a eventos. Aqui a lógica de negócio vive dentro do stream, e não em um serviço que consulta um banco: matching de corridas, precificação dinâmica, motores de alerta, atualização de feature stores para modelos de machine learning.</li>
<li>IA sobre streams. Inferência de modelos aplicada a eventos conforme eles passam, e o Flink Agents, subprojeto novo voltado a agentes de IA que reagem a eventos em tempo real.</li>
</ul>
<h2>A arquitetura, em poucas peças<span class="hx:absolute hx:-mt-20" id="a-arquitetura-em-poucas-peças"></span>
    <a href="#a-arquitetura-em-poucas-pe%c3%a7as" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><table>
  <thead>
      <tr>
          <th style="text-align: left">Peça</th>
          <th style="text-align: left">O que faz</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><strong>JobManager</strong></td>
          <td style="text-align: left">O coordenador. Recebe o job, transforma o grafo lógico em grafo de execução, distribui as tarefas, dispara os checkpoints e coordena a recuperação de falhas.</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>TaskManager</strong></td>
          <td style="text-align: left">O trabalhador. É um processo JVM que executa as tarefas de verdade e guarda o estado local.</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Task Slot</strong></td>
          <td style="text-align: left">A unidade de paralelismo dentro de um TaskManager. Um TaskManager com 4 slots consegue rodar 4 fatias de trabalho em paralelo.</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Client</strong></td>
          <td style="text-align: left">Quem empacota o job e submete ao JobManager. Depois disso, ele pode ir embora.</td>
      </tr>
  </tbody>
</table>
<p>Um job é representado como um <strong>grafo de operadores</strong>, composto por fontes (<code>source</code>), transformações (<code>map</code>, <code>filter</code>, <code>keyBy</code>, <code>window</code>) e destinos (<code>sink</code>). A partir desse grafo, o Flink determina o paralelismo de cada operador e distribui suas instâncias entre os slots disponíveis no cluster.</p>
<p>O <code>keyBy</code> merece atenção especial porque é responsável pelo particionamento dos dados. Quando você define <code>keyBy(event -&gt; event.getUserId())</code>, está determinando que eventos com a mesma chave sejam direcionados para a mesma instância paralela do operador seguinte. Esse particionamento é fundamental para o processamento com estado por chave, pois permite que cada instância mantenha e gerencie o estado associado às chaves sob sua responsabilidade, sem a necessidade de coordenação distribuída a cada evento.</p>
<h2>Fontes e destinos<span class="hx:absolute hx:-mt-20" id="fontes-e-destinos"></span>
    <a href="#fontes-e-destinos" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Fonte e destino são os dois pontos em que o job encosta no mundo. Na linha 2.x eles passam por uma API só, <code>env.fromSource(...)</code> de um lado e <code>stream.sinkTo(...)</code> do outro.</p>
<p>As três fontes que aparecem em quase todo projeto:</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">Fonte</th>
          <th style="text-align: left">Para que serve</th>
          <th style="text-align: left">O que ela guarda em estado</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><strong>KafkaSource</strong></td>
          <td style="text-align: left">O caso comum. Lê um ou mais tópicos, com um consumer group.</td>
          <td style="text-align: left">O offset de cada partição</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Flink CDC</strong></td>
          <td style="text-align: left">Lê o log de transações de um banco e transforma cada mudança de linha em evento.</td>
          <td style="text-align: left">A posição no binlog ou no WAL</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>FileSource</strong></td>
          <td style="text-align: left">Lê arquivos de um bucket ou do HDFS, em lote ou monitorando o diretório.</td>
          <td style="text-align: left">Quais arquivos e que trecho de cada um já foi lido</td>
      </tr>
  </tbody>
</table>
<p>E os três destinos:</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">Destino</th>
          <th style="text-align: left">Para que serve</th>
          <th style="text-align: left">Garantia que oferece</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><strong>KafkaSink</strong></td>
          <td style="text-align: left">Publica o resultado em outro tópico, para o próximo job ou para quem consome.</td>
          <td style="text-align: left">Exactly-once, via transações do Kafka</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>JdbcSink</strong></td>
          <td style="text-align: left">Grava em Postgres, MySQL e afins, tipicamente o resultado agregado.</td>
          <td style="text-align: left">Idempotência, quando existe chave primária</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>FileSink</strong></td>
          <td style="text-align: left">Escreve Parquet ou JSON particionado em armazenamento de objetos.</td>
          <td style="text-align: left">Exactly-once, confirmando os arquivos no checkpoint</td>
      </tr>
  </tbody>
</table>
<p>O formato do código é sempre o mesmo, um builder de um lado e outro do outro:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">KafkaSource</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">source</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">KafkaSource</span><span class="p">.</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span><span class="n">builder</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">setBootstrapServers</span><span class="p">(</span><span class="s">&#34;kafka:9092&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">setTopics</span><span class="p">(</span><span class="s">&#34;otlp-spans&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">setGroupId</span><span class="p">(</span><span class="s">&#34;flink-otlp&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">setStartingOffsets</span><span class="p">(</span><span class="n">OffsetsInitializer</span><span class="p">.</span><span class="na">earliest</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">setValueOnlyDeserializer</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">SimpleStringSchema</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">build</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">KafkaSink</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">sink</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">KafkaSink</span><span class="p">.</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span><span class="n">builder</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">setBootstrapServers</span><span class="p">(</span><span class="s">&#34;kafka:9092&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">setRecordSerializer</span><span class="p">(</span><span class="n">KafkaRecordSerializationSchema</span><span class="p">.</span><span class="na">builder</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">.</span><span class="na">setTopic</span><span class="p">(</span><span class="s">&#34;otlp-alerts&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">.</span><span class="na">setValueSerializationSchema</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">SimpleStringSchema</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">.</span><span class="na">build</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">setDeliveryGuarantee</span><span class="p">(</span><span class="n">DeliveryGuarantee</span><span class="p">.</span><span class="na">EXACTLY_ONCE</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">setTransactionalIdPrefix</span><span class="p">(</span><span class="s">&#34;otlp-&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">build</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="n">env</span><span class="p">.</span><span class="na">fromSource</span><span class="p">(</span><span class="n">source</span><span class="p">,</span><span class="w"> </span><span class="n">watermarkStrategy</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;otlp-spans&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="p">.</span><span class="na">process</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">AnomalyDetector</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="p">.</span><span class="na">sinkTo</span><span class="p">(</span><span class="n">sink</span><span class="p">);</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Duas coisas nesse código não são detalhe de sintaxe. A primeira é que a fonte não faz commit de offset por conta própria: o <strong>offset é estado do Flink</strong>, entra no checkpoint junto com o resto e volta com ele numa recuperação. É por isso que não se gerencia offset na mão aqui, e é por isso também que o painel de consumer lag do Kafka pode discordar da realidade do job, já que o commit para o Kafka é só informativo.</p>
<p>A segunda é que o exactly-once no destino é uma propriedade do destino, não do Flink. O <code>KafkaSink</code> só alcança isso porque escreve dentro de uma transação que é confirmada quando o checkpoint fecha, e por isso ele exige um <code>transactionalIdPrefix</code> estável entre reinícios. O <code>FileSink</code> funciona pela mesma lógica: os arquivos ficam com sufixo de &ldquo;em progresso&rdquo; até o checkpoint confirmar, o que costuma assustar quem olha o bucket no meio do caminho e acha que nada está sendo escrito. Já o <code>JdbcSink</code> não tem transação distribuída, então a garantia que ele oferece depende de você escrever com chave primária e deixar a repetição ser inofensiva.</p>
<p>Vale insistir na parte do versionamento, porque ela morde. Com exceção dos conectores de sistema de arquivos e do <code>datagen</code>, que vêm dentro da distribuição, cada conector é um projeto separado, com repositório próprio, release próprio e numeração que não tem relação nenhuma com a do Flink. O <code>flink-connector-kafka</code> está na 5.0.0 enquanto o Flink está na 2.3. Os dois números não conversam, e não existe &ldquo;a versão do conector que combina com a minha&rdquo; por dedução.</p>
<p>O que existe é uma matriz de compatibilidade, publicada na página de downloads, e conferir essa matriz é passo obrigatório antes de escolher a versão do Flink de um projeto novo. O motivo é que a migração para a linha 2.x não foi simultânea, e boa parte do ecossistema ficou para trás. Alguns exemplos de como isso estava quando escrevi:</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">Conector</th>
          <th style="text-align: left">Versão</th>
          <th style="text-align: left">Flink compatível</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left">Kafka</td>
          <td style="text-align: left">5.0.0</td>
          <td style="text-align: left">2.1.x e 2.2.x</td>
      </tr>
      <tr>
          <td style="text-align: left">JDBC</td>
          <td style="text-align: left">4.1.0</td>
          <td style="text-align: left">2.1.x e 2.2.x</td>
      </tr>
      <tr>
          <td style="text-align: left">Elasticsearch</td>
          <td style="text-align: left">4.0.0</td>
          <td style="text-align: left">2.0.x</td>
      </tr>
      <tr>
          <td style="text-align: left">Prometheus</td>
          <td style="text-align: left">1.0.0</td>
          <td style="text-align: left">1.19.x e 1.20.x</td>
      </tr>
  </tbody>
</table>
<p>O conector do Prometheus é o caso mais eloquente, e é um que me interessa de perto, porque ele é a saída natural para métricas derivadas de telemetria. Ele existe, é oficial, e escreve via a API de remote write do Prometheus. É só sink, não há fonte correspondente, o que faz sentido: o Prometheus é onde a série temporal termina, não de onde ela vem. Só que a 1.0.0 ainda é da linha 1.x do Flink e não atravessou para a 2.x. Quem quiser esse destino hoje escolhe entre ficar na 1.20, que é LTS, ou escrever a integração na mão.</p>
<p>E repare que nem o Kafka lista a 2.3 na tabela acima. A defasagem de um release entre o núcleo e os conectores é o estado normal das coisas, não uma exceção. Se o seu pipeline depende de um conector específico, é ele que decide qual versão do Flink você pode rodar, e não o contrário.</p>
<h2>Paralelismo<span class="hx:absolute hx:-mt-20" id="paralelismo"></span>
    <a href="#paralelismo" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Paralelismo é o número de instâncias de cada operador rodando ao mesmo tempo. Um job com paralelismo 8 tem oito cópias do source, oito do map e oito do sink, cada uma processando uma fatia dos dados e ocupando um slot. Cada uma dessas cópias é uma subtask, e é por isso que a interface web mostra métricas repetidas com índices de 0 a 7.</p>
<p>Ele pode ser definido em três alturas, da mais ampla para a mais específica. No job inteiro, com <code>env.setParallelism(8)</code> no código ou <code>parallelism.default</code> no <code>config.yaml</code>. No deploy, pelo campo <code>parallelism</code> do <code>FlinkDeployment</code>, que é o que aparece no manifesto mais adiante. E operador a operador, encadeando <code>.setParallelism(2)</code> na chamada, o que sobrescreve os anteriores. O caso típico do override é um sink que conversa com um banco que não aguenta oitenta conexões simultâneas: o resto do job continua largo e só a escrita é estrangulada de propósito.</p>
<p>Para o valor inicial, um ponto de partida razoável é igualar o paralelismo ao número de partições do tópico Kafka de entrada. A razão de não passar disso é específica e vale entender, porque o sintoma é confuso: as subtasks de fonte que sobram não recebem partição nenhuma, ficam ociosas e nunca emitem watermark. Como o watermark de um operador é o menor entre todas as suas entradas, uma fonte parada segura o relógio do job inteiro, e o resultado é uma janela que nunca fecha e um job que parece travado sem nenhum erro no log. Dá para contornar declarando <code>withIdleness()</code> na estratégia de watermark, que manda o Flink ignorar as entradas silenciosas, MAS É MAIS SIMPLES NÃO CRIAR O PROBLEMA. Ficar abaixo do número de partições também tem custo, menor: você deixa vazão do tópico na mesa, porque uma subtask passa a ler mais de uma partição.</p>
<p>Depois disso, o ajuste é por medição, não por conta. Os dois números que interessam na interface web são o <em>backpressure</em>, que mostra qual operador está segurando o fluxo, e o <em>busy time</em>, que mostra a fração do tempo em que cada subtask esteve realmente trabalhando em vez de esperando. Aumentar o paralelismo de um operador que passa o dia ocioso não melhora nada. Falo mais sobre esse dimensionamento na seção de CPU e slots.</p>
<p>Falta uma peça para o rescale fazer sentido, e ela é interna: o Flink não distribui chave por chave. Ele passa cada chave por um hash e a coloca em um <strong>key group</strong>, e o que é distribuído entre as subtasks são os key groups, em faixas contíguas. O key group é a unidade de movimentação de estado, ou seja, quando você muda o paralelismo, o que migra de uma máquina para outra são grupos inteiros, nunca chaves avulsas. Não se mexe nisso diretamente quase nunca, mas o termo aparece em log e em qualquer conversa sobre rescaling.</p>
<p>O número de key groups que existem é exatamente o <code>maxParallelism</code>. Daí vêm as duas propriedades que importam. Ele é o teto absoluto do paralelismo, porque não dá para ter mais subtasks do que grupos para distribuir entre elas. E ele não pode mudar depois, porque alterar a quantidade de grupos muda a que grupo cada chave pertence, o que invalida todo o estado já salvo.</p>
<p>A armadilha está no valor padrão. Sem configuração, o Flink calcula esse número a partir do paralelismo inicial: algo em torno de uma vez e meia ele, arredondado para cima, com piso de 128 e teto de 32768. Um job que sobe com paralelismo 8 fica com 128 e ninguém repara, porque nada quebra. O problema chega no dia em que esse job precisa de paralelismo 200, e a única saída é reconstruir o estado do zero.</p>
<p>Definir na mão desde a primeira execução resolve, e <code>env.setMaxParallelism(1024)</code> é um valor confortável. O que não vale é colocar 32768 por precaução: alguns state backends mantêm estruturas internas que crescem com o número de key groups, então exagerar cobra em desempenho. Um cuidado extra que sai de graça é escolher um valor divisível pelo paralelismo pretendido, porque assim os grupos se distribuem por igual. Com 128 grupos e paralelismo 12, algumas subtasks ficam com 11 e outras com 10, uma assimetria pequena mas evitável.</p>
<h2>Redistribuição e data skew<span class="hx:absolute hx:-mt-20" id="redistribuição-e-data-skew"></span>
    <a href="#redistribui%c3%a7%c3%a3o-e-data-skew" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Entre dois operadores existe sempre uma decisão sobre para qual subtask seguinte cada evento vai. Na maior parte do tempo ela é tomada pelo Flink sem que você peça, mas dá para escolher, e as opções são poucas:</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">Estratégia</th>
          <th style="text-align: left">O que faz</th>
          <th style="text-align: left">Quando aparece</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><code>forward</code></td>
          <td style="text-align: left">O evento fica na mesma subtask. Nada atravessa a rede</td>
          <td style="text-align: left">Entre operadores adjacentes de mesmo paralelismo. É o que permite o chaining</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>hash</code></td>
          <td style="text-align: left">Roteia pela chave, sempre a mesma chave para a mesma subtask</td>
          <td style="text-align: left">Todo <code>keyBy</code>, ou seja, sempre que existe estado por chave</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>rebalance</code></td>
          <td style="text-align: left">Round-robin entre todas as subtasks seguintes</td>
          <td style="text-align: left">Para reequilibrar depois de uma fonte que chega desbalanceada</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>rescale</code></td>
          <td style="text-align: left">Round-robin, mas só entre as subtasks vizinhas, preservando localidade</td>
          <td style="text-align: left">O mesmo objetivo do <code>rebalance</code>, evitando tráfego entre máquinas</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>broadcast</code></td>
          <td style="text-align: left">Cada evento vai para todas as subtasks seguintes</td>
          <td style="text-align: left">Distribuir regras ou configuração para todas as instâncias</td>
      </tr>
  </tbody>
</table>
<p>O <code>broadcast</code> merece nota porque é a base de um padrão inteiro: um stream pequeno de regras é transmitido para todas as subtasks, que o guardam em estado, enquanto o stream grande de eventos passa particionado por chave. É assim que se troca a lógica de um job sem redeploy.</p>
<p>Vale lembrar também que <code>rebalance</code> e <code>rescale</code> ganharam distribuição adaptativa na 2.3, olhando a carga real do destino em vez de fazer round-robin cego, e é uma opção que precisa ser ligada, como descrevo no post sobre o Flink 2.3.</p>
<p>Nada disso resolve o problema que mais dói, que é o <strong>data skew</strong>. Skew é quando a distribuição das chaves é desigual: um cliente que sozinho responde por metade das transações, um produto que viralizou, um tenant gigante no meio de mil pequenos. Como o <code>keyBy</code> garante que a mesma chave vai sempre para a mesma subtask, essa subtask vira o gargalo, as outras ficam ociosas, e o job inteiro anda na velocidade da mais lenta. Aumentar o paralelismo não ajuda em nada, porque a chave pesada continua indo para um lugar só.</p>
<p>O diagnóstico é direto na interface web, e é por isso que o busy time importa tanto: skew aparece como backpressure concentrado em uma subtask e como uma instância em 100% de ocupação enquanto as vizinhas estão em 10%. Se todas estivessem em 90%, o problema seria de capacidade, e a resposta seria outra.</p>
<p>As saídas são três, em ordem de esforço. A primeira, e a mais usada, é agregar em duas fases: você acrescenta um sufixo aleatório à chave, algo como <code>cliente-42#0</code> até <code>cliente-42#9</code>, agrega parcialmente sobre essa chave inflada, e só então agrega de novo sobre a chave real. O trabalho pesado passa a ser dividido por dez subtasks, e a segunda etapa recebe apenas dez resultados parciais por chave em vez de milhões de eventos. A segunda, quando se está no SQL, é deixar o motor fazer isso: ligar o mini-batch e a otimização de agregação em duas fases resolve o caso comum sem reescrever consulta nenhuma, e existe até uma opção específica para dividir agregações com <code>DISTINCT</code>, que é a versão automática do mesmo truque. A terceira é aceitar que a chave-baleia é um caso à parte e tratá-la fora do fluxo normal, com um pipeline dedicado(clássica).</p>
<h2>Operator chaining<span class="hx:absolute hx:-mt-20" id="operator-chaining"></span>
    <a href="#operator-chaining" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O grafo que você escreve não é exatamente o grafo que roda. Antes de executar, o Flink funde operadores adjacentes em uma única task, e isso se chama <em>operator chaining</em>. Uma sequência como <code>map</code>, <code>filter</code> e <code>flatMap</code> vira uma coisa só, executada na mesma thread.</p>
<p>A fusão só acontece sob três condições: a ligação entre os operadores precisa ser <code>forward</code>, eles precisam ter o mesmo paralelismo e precisam estar no mesmo grupo de compartilhamento de slot. Um <code>keyBy</code> ou um <code>rebalance</code> quebram a corrente, porque nesses casos o registro precisa mesmo sair de uma instância e ir para outra.</p>
<p>O ganho é maior do que parece à primeira vista. Dentro de uma corrente, passar um registro de um operador para o próximo é uma chamada de método, e o objeto continua sendo o mesmo na memória. Entre duas tasks, o registro é serializado, vai para um buffer de rede e é desserializado do outro lado, e isso acontece mesmo quando as duas estão dentro do mesmo TaskManager. O chaining elimina esse custo inteiro.</p>
<p>É também a explicação para uma confusão comum na interface web: as caixas do grafo não correspondem aos seus operadores, elas correspondem às correntes. Você escreve seis operadores e vê duas caixas, cada uma listando dentro de si os nomes que absorveu.</p>
<p>A armadilha aparece na hora de investigar um problema. Métricas e backpressure são medidos por task, não por operador, então uma corrente inteira reporta um número só, e não dá para saber qual dos operadores fundidos está segurando o fluxo. Para descobrir, dá para quebrar a corrente de propósito: <code>.disableChaining()</code> isola um operador dos dois lados, e <code>.startNewChain()</code> começa uma corrente nova a partir dele. Isso é ferramenta de diagnóstico, não configuração para deixar ligada. O custo de serialização que você reintroduziu é real, então vale reverter depois de medir.</p>
<h2>Rich functions<span class="hx:absolute hx:-mt-20" id="rich-functions"></span>
    <a href="#rich-functions" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Toda interface de função tem uma versão &ldquo;rich&rdquo;: <code>MapFunction</code> tem <code>RichMapFunction</code>, <code>FilterFunction</code> tem <code>RichFilterFunction</code>, e assim por diante. A diferença é que a versão rich acrescenta ciclo de vida e contexto, e é isso que separa uma função que apenas transforma um registro de uma função que precisa de recursos externos ou de memória.</p>
<p>O <code>open()</code> é chamado uma vez por instância paralela, antes do primeiro evento chegar. É o lugar de abrir conexão com um banco, carregar um cache de enriquecimento, compilar uma expressão regular e, principalmente, registrar os descritores de estado. Vale reparar no &ldquo;por instância paralela&rdquo;: com paralelismo 8 ele roda oito vezes, uma em cada subtask, e o que ele cria não é compartilhado entre elas. O <code>close()</code> é o par dele, chamado no encerramento, e serve para devolver o que foi aberto.</p>
<p>O <code>getRuntimeContext()</code> é a porta de entrada para o que o runtime sabe. É por ele que se chega ao estado por chave, às métricas customizadas que você queira expor, ao índice da subtask atual e ao paralelismo do operador.</p>
<p>Se isso soar familiar quando você chegar na seção de estado, é porque o exemplo de lá já usa os dois: o <code>AnomalyDetector</code> sobrescreve <code>open()</code> e chama <code>getRuntimeContext().getState(...)</code> para criar o <code>ValueState</code>. Ele não tem &ldquo;Rich&rdquo; no nome, mas <code>KeyedProcessFunction</code> estende <code>AbstractRichFunction</code> e herda o mesmo ciclo de vida. As process functions já vêm ricas de fábrica.</p>
<p>Uma nota de versão que economiza tempo: até a 1.x a assinatura era <code>open(Configuration parameters)</code>. Na 2.x ela foi removida, e o método passou a receber um <code>OpenContext</code>. Exemplo copiado de tutorial antigo não compila por causa disso.</p>
<h2>Tempo: a parte que quebra a intuição<span class="hx:absolute hx:-mt-20" id="tempo-a-parte-que-quebra-a-intuição"></span>
    <a href="#tempo-a-parte-que-quebra-a-intui%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Essa foi a seção que mais me fez parar e reler. Em streaming existem, no mínimo, dois tempos diferentes:</p>
<ul>
<li><strong>Event time:</strong> quando o evento aconteceu de verdade. Está gravado dentro do próprio evento.</li>
<li><strong>Processing time:</strong> quando o Flink viu o evento. É o relógio da máquina.</li>
</ul>
<p>Parece detalhe, mas não é. O celular de alguém ficou sem sinal no metrô e sincronizou vinte minutos depois. Um broker teve um pico de latência. Uma partição do Kafka ficou atrasada em relação às outras. Em todos esses casos, os eventos chegam <strong>fora de ordem</strong> e <strong>atrasados</strong>.</p>
<p>Se você agrupar por processing time, o resultado depende de quando o job rodou. Reprocessar o mesmo dia amanhã dá um número diferente. Se você agrupar por event time, o resultado é o mesmo sempre, independente de quando o processamento aconteceu. Essa reprodutibilidade é o motivo de event time ser o padrão em quase todo pipeline sério.</p>
<p>Mas isso cria um problema novo: se os eventos chegam fora de ordem, como saber quando uma janela de tempo pode ser fechada? Nunca chega um evento dizendo &ldquo;acabou, pode contar&rdquo;.</p>
<h2>Watermarks<span class="hx:absolute hx:-mt-20" id="watermarks"></span>
    <a href="#watermarks" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A resposta do Flink são as <strong>watermarks</strong>. Uma watermark é um marcador que viaja junto com o stream carregando uma afirmação:</p>
<blockquote>
  <p>&ldquo;Acredito que não vou mais receber eventos com timestamp anterior a T.&rdquo;</p>

</blockquote>
<p>É uma aposta, não uma certeza. E é exatamente por ser uma aposta que ela é útil: ela transforma uma pergunta impossível (&ldquo;já chegou tudo?&rdquo;) em uma decisão configurável (&ldquo;quanto atraso eu tolero?&rdquo;).</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">WatermarkStrategy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="o">&lt;</span><span class="n">Event</span><span class="o">&gt;</span><span class="n">forBoundedOutOfOrderness</span><span class="p">(</span><span class="n">Duration</span><span class="p">.</span><span class="na">ofSeconds</span><span class="p">(</span><span class="n">10</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">withTimestampAssigner</span><span class="p">((</span><span class="n">event</span><span class="p">,</span><span class="w"> </span><span class="n">ts</span><span class="p">)</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">event</span><span class="p">.</span><span class="na">getTimestamp</span><span class="p">());</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Esse código diz: tolere até 10 segundos de desordem. Se o maior timestamp já visto é <code>12:00:30</code>, a watermark vale <code>12:00:20</code>, e qualquer janela que termine antes disso pode ser fechada e emitida.</p>
<p>O trade-off aparece na hora:</p>
<ul>
<li>Watermark <strong>agressiva</strong> (pouco atraso tolerado) → resultado rápido, mais eventos perdidos por chegarem tarde.</li>
<li>Watermark <strong>conservadora</strong> (muito atraso tolerado) → resultado correto, mas você espera mais para ver qualquer coisa.</li>
</ul>
<p>Não existe valor certo. Existe o valor que combina com o seu negócio, e essa é uma decisão de produto disfarçada de configuração técnica.</p>
<p>Para eventos que chegam após o avanço da watermark, o Flink oferece mecanismos específicos para tratar dados atrasados. O <code>allowedLateness</code> permite manter a janela aberta por um período adicional, possibilitando o processamento de eventos tardios e a atualização dos resultados já produzidos. Outra alternativa é utilizar <strong>side output</strong>, direcionando os eventos que ultrapassaram o limite de atraso para um stream separado. Esse mecanismo pode ser útil para auditoria, métricas de qualidade dos dados ou para identificar problemas na configuração da estratégia de watermark.</p>
<h2>Janelas<span class="hx:absolute hx:-mt-20" id="janelas"></span>
    <a href="#janelas" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Como o stream é infinito, agregações só fazem sentido dentro de recortes.</p>
<ul>
<li><strong>Tumbling:</strong> janelas fixas que não se sobrepõem. &ldquo;A cada 5 minutos, quantos pedidos?&rdquo;</li>
<li><strong>Sliding:</strong> janelas fixas que se sobrepõem. &ldquo;Nos últimos 10 minutos, atualizando a cada 1 minuto.&rdquo; Um mesmo evento cai em várias janelas.</li>
<li><strong>Session:</strong> janelas definidas por inatividade. &ldquo;Agrupe a atividade de um usuário até ele ficar 30 minutos parado.&rdquo; O tamanho da janela é dado pelos dados, não por você.</li>
</ul>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">stream</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">keyBy</span><span class="p">(</span><span class="n">Event</span><span class="p">::</span><span class="n">getUserId</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">window</span><span class="p">(</span><span class="n">TumblingEventTimeWindows</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="n">Time</span><span class="p">.</span><span class="na">minutes</span><span class="p">(</span><span class="n">5</span><span class="p">)))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">aggregate</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">EventCounter</span><span class="p">());</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Um detalhe que eu não tinha entendido de cara: prefira <code>reduce</code>/<code>aggregate</code> a <code>process</code> quando puder. O <code>process</code> recebe todos os eventos da janela de uma vez, o que significa guardar todos eles em estado até o fechamento. Já <code>aggregate</code> mantém apenas o acumulador. Em janelas grandes, essa diferença é a diferença entre um job estável e um job que morre de OOM.</p>
<h2>ProcessFunction<span class="hx:absolute hx:-mt-20" id="processfunction"></span>
    <a href="#processfunction" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Os operadores prontos cobrem muita coisa, mas eles impõem uma forma. <code>map</code> transforma um evento em outro, <code>window</code> agrupa por recorte de tempo, <code>aggregate</code> acumula. Quando a lógica não cabe em nenhum desses moldes, o caminho é descer um nível e escrever uma <code>ProcessFunction</code>, que é o operador de mais baixo nível e mais poderoso da DataStream API. É a faca suíça do Flink, e não é exagero dizer que quase toda lógica de negócio realmente complicada acaba dentro de uma.</p>
<p>O que ela dá, a cada evento, são três coisas que os operadores prontos não dão juntas: acesso direto ao estado da chave, a capacidade de registrar timers e a de emitir para side outputs. As três chegam pelo mesmo lugar, aquele parâmetro <code>ctx</code> que aparece na assinatura do <code>processElement</code>.</p>
<p>O timer é a parte que muda o que dá para construir, porque é ele que permite reagir à passagem do tempo, e não só à chegada de um evento. Você registra um instante futuro e o Flink chama o seu <code>onTimer</code> quando ele chega:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Override</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">processElement</span><span class="p">(</span><span class="n">Event</span><span class="w"> </span><span class="n">event</span><span class="p">,</span><span class="w"> </span><span class="n">Context</span><span class="w"> </span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="n">Collector</span><span class="o">&lt;</span><span class="n">Alert</span><span class="o">&gt;</span><span class="w"> </span><span class="n">out</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">ctx</span><span class="p">.</span><span class="na">timerService</span><span class="p">().</span><span class="na">registerEventTimeTimer</span><span class="p">(</span><span class="n">event</span><span class="p">.</span><span class="na">getTimestamp</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">600_000</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nd">@Override</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">onTimer</span><span class="p">(</span><span class="kt">long</span><span class="w"> </span><span class="n">timestamp</span><span class="p">,</span><span class="w"> </span><span class="n">OnTimerContext</span><span class="w"> </span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="n">Collector</span><span class="o">&lt;</span><span class="n">Alert</span><span class="o">&gt;</span><span class="w"> </span><span class="n">out</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// dez minutos de event time se passaram desde aquele evento</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Duas propriedades desse mecanismo importam. A primeira é que um timer de event time não dispara quando o relógio da máquina chega lá, e sim quando a watermark passa daquele instante, o que amarra os timers na mesma noção de tempo das janelas: se a watermark não avança, o timer não dispara, e voltamos ao problema da fonte ociosa que descrevi antes. A segunda é que timers são estado, pertencem a uma chave e entram no checkpoint. Um job que reinicia não perde os timers pendentes, e é isso que torna viável escrever &ldquo;se não acontecer nada nos próximos trinta minutos, alerte&rdquo; e confiar na frase.</p>
<p>O side output é a segunda porta. Já apareceu aqui como destino de eventos atrasados, mas serve para qualquer bifurcação: registros que não passaram na validação, eventos que dispararam uma regra, amostras para auditoria. Você declara um <code>OutputTag</code>, chama <code>ctx.output(tag, valor)</code> e recupera esse fluxo depois com <code>getSideOutput(tag)</code>. A vantagem sobre um <code>filter</code> duplicado é que o evento é examinado uma vez só, e a vantagem sobre lançar uma exceção é que o job não morre por causa de um registro malformado.</p>
<p>Existem algumas variantes, e escolher a errada é um erro comum. A <code>KeyedProcessFunction</code> é a que vem depois de um <code>keyBy</code> e a única com estado por chave e timers. A <code>ProcessFunction</code> pura funciona sobre um stream não particionado e não tem nem uma coisa nem outra. A <code>CoProcessFunction</code> recebe dois streams e é o caminho para juntar um fluxo de eventos com um fluxo de regras ou de dados de referência. E a <code>ProcessWindowFunction</code> é a que roda no fechamento de uma janela, com todos os eventos dela em mãos.</p>
<p>O preço de descer para esse nível é que o Flink para de cuidar das coisas por você. Uma janela sabe quando terminar e joga fora o que acumulou; um estado que você criou dentro de uma <code>ProcessFunction</code> fica lá para sempre, para cada chave que já apareceu, até você apagá-lo explicitamente ou configurar TTL. É a mesma liberdade e o mesmo risco de qualquer abstração de baixo nível, e é o motivo de a recomendação continuar sendo usar o operador pronto sempre que ele couber.</p>
<h2>Estado<span class="hx:absolute hx:-mt-20" id="estado"></span>
    <a href="#estado" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Comecei a estudar o Flink escrevendo, antes dele, meu próprio consumer Kafka. A ideia era responder uma pergunta só: <em>o que um framework de processamento distribuído resolve além de ler eventos de um tópico ?</em></p>
<p>A resposta apareceu rápido, e ela é uma lista. Gerenciamento de estado, paralelismo, particionamento, tolerância a falhas, recuperação depois de uma queda, event time, watermarks, coordenação entre as etapas do processamento. No consumer manual, cada item desses vira código seu. No Flink, quase todos fazem parte do runtime, e o que sobra para a aplicação é a lógica de processamento.</p>
<p>Qualquer processamento mais interessante em streaming precisa manter algum tipo de memória, seja para contar eventos, realizar deduplicação, detectar padrões, correlacionar dados ou comparar valores anteriores. Em um consumer tradicional, essa responsabilidade acaba ficando muito mais próxima da aplicação. No Flink, <strong>o estado é gerenciado pelo runtime e pode ser distribuído, particionado por chave, armazenado de forma persistente e recuperado após uma falha por meio dos mecanismos de checkpoint e recuperação</strong>. Isso muda bastante a forma de construir uma aplicação de streaming, principalmente quando começamos a pensar em escala, falhas e processamento contínuo.</p>
<p>O Flink oferece estado como uma primitiva de primeira classe, com tipos como <code>ValueState</code>, <code>ListState</code> e <code>MapState</code>, sempre associados a uma chave depois do <code>keyBy</code>.</p>
<p><code>Que caralhos é uma Primitiva de primeira classe ??</code></p>
<p>Vem da teoria de linguagens de programação. Algo é de primeira classe quando o sistema o trata como um cidadão pleno, com todos os direitos, em vez de um apêndice que você tem que gerenciar por fora.. significa que você não precisa de um banco. Aqui está a parte que realmente importa. A alternativa óbvia seria guardar esse acúmulo num Redis ou Postgres externo. O Flink faz diferente: o estado vive dentro do próprio operador, no processo do TaskManager, gerenciado pelo framework. Isso traz três consequências.</p>
<p>Primeira, é local. Ler estado é acesso a memória ou a um RocksDB no disco local da máquina, não uma chamada de rede. Isso é a diferença entre microssegundos e milissegundos por evento, e a razão pela qual o Flink consegue processar milhões de eventos por segundo com lógica que depende de histórico.</p>
<p>Segunda, entra nos checkpoints. Periodicamente o Flink tira um snapshot consistente de todo o estado de todos os operadores e grava num storage durável (S3, HDFS). Se um TaskManager morrer, o job reinicia do último checkpoint com o estado exatamente como estava, e os offsets do Kafka voltam junto. Isso é o que sustenta a semântica exactly-once que mencionamos antes. Com Redis externo, você teria que resolver essa consistência entre &ldquo;o que já consumi do Kafka&rdquo; e &ldquo;o que já escrevi no Redis&rdquo; na mão, e é notoriamente difícil.</p>
<p>Terceira, é redimensionável. Ao aumentar o paralelismo do job, o Flink redistribui o estado entre as novas instâncias automaticamente, a partir de um savepoint.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">AnomalyDetector</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">KeyedProcessFunction</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">Event</span><span class="p">,</span><span class="w"> </span><span class="n">Alert</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kd">transient</span><span class="w"> </span><span class="n">ValueState</span><span class="o">&lt;</span><span class="n">Double</span><span class="o">&gt;</span><span class="w"> </span><span class="n">lastValue</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Override</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">open</span><span class="p">(</span><span class="n">OpenContext</span><span class="w"> </span><span class="n">openContext</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">lastValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">getRuntimeContext</span><span class="p">().</span><span class="na">getState</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">new</span><span class="w"> </span><span class="n">ValueStateDescriptor</span><span class="o">&lt;&gt;</span><span class="p">(</span><span class="s">&#34;last-value&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">Double</span><span class="p">.</span><span class="na">class</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Override</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">processElement</span><span class="p">(</span><span class="n">Event</span><span class="w"> </span><span class="n">event</span><span class="p">,</span><span class="w"> </span><span class="n">Context</span><span class="w"> </span><span class="n">ctx</span><span class="p">,</span><span class="w"> </span><span class="n">Collector</span><span class="o">&lt;</span><span class="n">Alert</span><span class="o">&gt;</span><span class="w"> </span><span class="n">out</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">Double</span><span class="w"> </span><span class="n">previous</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">lastValue</span><span class="p">.</span><span class="na">value</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">previous</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">null</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">event</span><span class="p">.</span><span class="na">getValue</span><span class="p">()</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">previous</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">3</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">out</span><span class="p">.</span><span class="na">collect</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">Alert</span><span class="p">(</span><span class="n">event</span><span class="p">,</span><span class="w"> </span><span class="n">previous</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">lastValue</span><span class="p">.</span><span class="na">update</span><span class="p">(</span><span class="n">event</span><span class="p">.</span><span class="na">getValue</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Esse estado é <strong>particionado por chave</strong>: cada usuário tem o seu próprio <code>lastValue</code>, e o operador só enxerga a chave do evento atual. Também dá para configurar TTL, o que evita o problema clássico de estado que só cresce até estourar o disco.</p>
<p>Onde esse estado fica guardado é uma escolha, e são três opções na linha 2.x:</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">Backend</th>
          <th style="text-align: left">Onde o estado vive</th>
          <th style="text-align: left">A favor</th>
          <th style="text-align: left">Contra</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><strong>HashMapStateBackend</strong></td>
          <td style="text-align: left">Heap da JVM, como objetos Java</td>
          <td style="text-align: left">O mais rápido, porque não serializa nada a cada acesso</td>
          <td style="text-align: left">Limitado pela memória e sujeito a pausas de GC. Não faz checkpoint incremental</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>EmbeddedRocksDBStateBackend</strong></td>
          <td style="text-align: left">Disco local, em uma LSM tree embarcada</td>
          <td style="text-align: left">Escala até o tamanho do disco e faz checkpoint incremental</td>
          <td style="text-align: left">Serializa em toda leitura e escrita, e quer disco rápido</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>ForStStateBackend</strong></td>
          <td style="text-align: left">Armazenamento remoto, com cache local</td>
          <td style="text-align: left">Rescale e recuperação quase imediatos, TaskManager leve</td>
          <td style="text-align: left">Novo e ainda experimental. A latência de rede é compensada por acesso assíncrono</td>
      </tr>
  </tbody>
</table>
<p>O padrão, se você não configurar nada, é o <code>HashMapStateBackend</code>. Isso costuma surpreender quem leu que &ldquo;o Flink usa RocksDB&rdquo;, e a troca é uma linha em <code>state.backend.type</code>, que aceita <code>hashmap</code>, <code>rocksdb</code> ou <code>forst</code>.</p>
<p>A regra prática que eu tirei disso tem três casos. Estado de poucos gigabytes com latência crítica pede HashMap, porque a ausência de serialização é justamente o que você está comprando. Estado grande, de dezenas de gigabytes a terabytes, pede RocksDB com checkpoint incremental, e vale notar que ele é o único dos dois clássicos que oferece isso: com HashMap, todo checkpoint copia o estado inteiro. E estado enorme em nuvem, com autoscaling agressivo, é o caso para avaliar o ForSt, que é a aposta da série 2.x e sobre o qual escrevo mais no final.</p>
<h2>Checkpoints e savepoints<span class="hx:absolute hx:-mt-20" id="checkpoints-e-savepoints"></span>
    <a href="#checkpoints-e-savepoints" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O estado só é confiável porque existe um mecanismo de tolerância a falhas por trás dele.</p>
<p>Periodicamente, o JobManager injeta <strong>barreiras de checkpoint</strong> nas fontes. Essas barreiras fluem pelo grafo junto com os dados; quando um operador recebe a barreira, ele persiste seu estado em armazenamento durável (S3, HDFS, o que for) e repassa a barreira adiante. Quando todos os operadores terminam, o checkpoint está completo.</p>
<p>O algoritmo é uma variação do <strong>Chandy-Lamport</strong> para snapshots distribuídos. Achei bonito descobrir que uma coisa tão prática está apoiada num paper de 1985.</p>
<p>Na falha, o Flink restaura o último checkpoint completo, reposiciona os offsets das fontes e retoma. Daí vem o famoso <strong>exactly-once</strong>, que eu entendi errado durante bastante tempo. Não significa que cada evento é processado uma única vez fisicamente. Na recuperação, eventos são sim reprocessados. Significa que o <strong>efeito sobre o estado</strong> é como se cada evento tivesse sido processado uma vez só. É uma garantia sobre o resultado, não sobre a execução.</p>
<p>Estender essa garantia até o sink é outro assunto, e depende de o destino suportar escrita transacional ou idempotente (Kafka com transações, por exemplo).</p>
<p>A diferença entre checkpoint e savepoint também levou um tempo para assentar:</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left"></th>
          <th style="text-align: left">Checkpoint</th>
          <th style="text-align: left">Savepoint</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left">Quem dispara</td>
          <td style="text-align: left">O Flink, automaticamente</td>
          <td style="text-align: left">Você, manualmente</td>
      </tr>
      <tr>
          <td style="text-align: left">Propósito</td>
          <td style="text-align: left">Recuperação de falha</td>
          <td style="text-align: left">Upgrade, migração, fork do job</td>
      </tr>
      <tr>
          <td style="text-align: left">Ciclo de vida</td>
          <td style="text-align: left">Gerenciado pelo Flink</td>
          <td style="text-align: left">Seu para sempre</td>
      </tr>
  </tbody>
</table>
<p>Savepoint é o que permite parar um job, subir uma versão nova do código e continuar exatamente de onde parou, com o estado intacto. Na prática, é isso que torna um job de streaming algo que se pode operar por anos.</p>
<h2>Flink SQL<span class="hx:absolute hx:-mt-20" id="flink-sql"></span>
    <a href="#flink-sql" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Nem tudo precisa de Java. O Flink expõe uma Table API e SQL sobre o mesmo motor, e a mesma consulta roda tanto sobre dados finitos quanto sobre um stream infinito:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">user_id</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">TUMBLE_START</span><span class="p">(</span><span class="n">event_time</span><span class="p">,</span><span class="w"> </span><span class="nb">INTERVAL</span><span class="w"> </span><span class="s1">&#39;5&#39;</span><span class="w"> </span><span class="k">MINUTE</span><span class="p">)</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">window_start</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">total</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="n">orders</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">GROUP</span><span class="w"> </span><span class="k">BY</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">user_id</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">TUMBLE</span><span class="p">(</span><span class="n">event_time</span><span class="p">,</span><span class="w"> </span><span class="nb">INTERVAL</span><span class="w"> </span><span class="s1">&#39;5&#39;</span><span class="w"> </span><span class="k">MINUTE</span><span class="p">);</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O conceito que sustenta isso é a <strong>dualidade stream/tabela</strong>: um stream é o log de mudanças de uma tabela, e uma tabela é o estado acumulado de um stream. São a mesma informação vista de dois ângulos.</p>
<p>Isso deixa de ser abstrato assim que você roda um <code>GROUP BY</code> no cliente SQL e olha a coluna <code>op</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>&#43;----&#43;------&#43;-------&#43;
| op | name | total |
&#43;----&#43;------&#43;-------&#43;
| &#43;I |    a |     1 |
| &#43;I |    b |     1 |
| -U |    a |     1 |
| &#43;U |    a |     2 |
&#43;----&#43;------&#43;-------&#43;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O <code>a</code> entra com 1, é retratado (<code>-U</code>) e reemitido com 2 (<code>+U</code>). Não é uma tabela sendo preenchida aos poucos, é o log de mudanças dela passando na tela. O resultado de uma query em streaming não é um valor final, é uma sequência de correções sobre o valor anterior.</p>
<p>São quatro tipos de linha ao todo: <code>+I</code> para inserção, <code>-U</code> e <code>+U</code> para o antes e o depois de uma atualização, e <code>-D</code> para remoção. E é a combinação de tipos que uma consulta produz que define com que tipo de stream você está lidando, o que na prática decide onde ele pode ser gravado:</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">Modo</th>
          <th style="text-align: left">O que sai da consulta</th>
          <th style="text-align: left">Exemplo</th>
          <th style="text-align: left">Onde grava</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><strong>Append-only</strong></td>
          <td style="text-align: left">Só <code>+I</code>. Nada do que já foi emitido muda.</td>
          <td style="text-align: left">Stream bruto de cliques, um <code>SELECT</code> com filtro.</td>
          <td style="text-align: left">Qualquer sink.</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Retract</strong></td>
          <td style="text-align: left"><code>+I</code>, e cada correção vira o par <code>-U</code> seguido de <code>+U</code>.</td>
          <td style="text-align: left"><code>GROUP BY</code> contínuo, sem janela, que revisa o total a cada evento.</td>
          <td style="text-align: left">Sink que entenda retração.</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Upsert</strong></td>
          <td style="text-align: left">Uma linha por chave, valendo como &ldquo;esta é a versão atual&rdquo;.</td>
          <td style="text-align: left">O mesmo <code>GROUP BY</code>, quando o sink declara uma chave primária.</td>
          <td style="text-align: left">Sink com chave primária.</td>
      </tr>
  </tbody>
</table>
<p>A diferença entre retract e upsert é sutil e importa na hora de escolher o destino. No modo retract, o Flink emite duas linhas para cada correção, e quem recebe precisa saber subtrair a antiga antes de somar a nova. No modo upsert, ele emite só a linha nova, e quem recebe sobrescreve o que tinha naquela chave. O segundo é mais barato e é o que bancos relacionais e índices de busca esperam, mas só é possível quando existe uma chave que identifique a linha.</p>
<p>Isso leva à armadilha clássica, que dá para prever antes de cair nela. Você escreve um <code>GROUP BY</code> contínuo e aponta o resultado para um tópico Kafka comum. O conector Kafka padrão é append-only, e o resultado da sua consulta é um fluxo de correções, não de inserções. O job não chega nem a subir: o planejador rejeita a consulta com uma mensagem do tipo &ldquo;table sink doesn&rsquo;t support consuming update changes which is produced by node GroupAggregate&rdquo;. A falha na hora do planejamento é uma boa notícia, porque o modo silencioso de errar isso seria gravar as retrações como se fossem eventos normais e ter um consumidor lá na frente somando tudo duas vezes.</p>
<p>Existem três saídas, e a escolha diz mais sobre o problema do que sobre o Flink. A primeira é usar um sink que aceite atualizações, como <code>upsert-kafka</code> ou JDBC com chave primária declarada. A segunda é fazer o resultado parar de mudar, agrupando por janela em vez de agrupar de forma contínua: quando a janela fecha, o valor é final e vira uma inserção só. A terceira, disponível a partir da 2.3, é converter explicitamente entre as formas com <code>FROM_CHANGELOG</code> e <code>TO_CHANGELOG</code>, que é o que descrevo no post sobre o Flink 2.3.</p>
<p>Para quem vem de dados, essa é a porta de entrada mais rápida, dá para chegar longe sem escrever uma linha de Java.</p>
<h2>In praxi (&ldquo;na prática&rdquo;)<span class="hx:absolute hx:-mt-20" id="in-praxi-na-prática"></span>
    <a href="#in-praxi-na-pr%c3%a1tica" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Subir um cluster local é mais simples do que eu imaginava. Comecei com dois <code>docker run</code> soltos, mas troquei por um <code>docker-compose.yml</code> assim que quis mais de um TaskManager:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">flink-lab</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">jobmanager</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">flink:2.3.0-java21</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">jobmanager</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="l">jobmanager</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;8081:8081&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">        FLINK_PROPERTIES=
</span></span></span><span class="line"><span class="cl"><span class="sd">        jobmanager.rpc.address: jobmanager
</span></span></span><span class="line"><span class="cl"><span class="sd">        rest.address: 0.0.0.0
</span></span></span><span class="line"><span class="cl"><span class="sd">        rest.bind-address: 0.0.0.0
</span></span></span><span class="line"><span class="cl"><span class="sd">        jobmanager.memory.process.size: 1024m</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">healthcheck</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">test</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;CMD-SHELL&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;curl -sf http://localhost:8081/overview || exit 1&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">interval</span><span class="p">:</span><span class="w"> </span><span class="l">5s</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">timeout</span><span class="p">:</span><span class="w"> </span><span class="l">3s</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">retries</span><span class="p">:</span><span class="w"> </span><span class="m">20</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">start_period</span><span class="p">:</span><span class="w"> </span><span class="l">10s</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">taskmanager</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">flink:2.3.0-java21</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="l">taskmanager</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">depends_on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">jobmanager</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">condition</span><span class="p">:</span><span class="w"> </span><span class="l">service_healthy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">deploy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">        FLINK_PROPERTIES=
</span></span></span><span class="line"><span class="cl"><span class="sd">        jobmanager.rpc.address: jobmanager
</span></span></span><span class="line"><span class="cl"><span class="sd">        taskmanager.numberOfTaskSlots: 2
</span></span></span><span class="line"><span class="cl"><span class="sd">        taskmanager.memory.process.size: 1728m</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Um <code>docker compose up -d</code> e a Web UI responde em <code>localhost:8081</code>.</p>
<p>Três escolhas aí não são detalhe. Duas réplicas de TaskManager com dois slots cada dão quatro slots, e é isso que faz o paralelismo deixar de ser sempre 1: sem folga de slots, todo grafo de job aparece na UI como uma fila reta. O <code>healthcheck</code> combinado com <code>depends_on: service_healthy</code> existe porque, sem ele, o TaskManager sobe antes de o JobManager estar de pé, falha ao registrar e enche o log de erro antes de funcionar na segunda tentativa. E os limites de memória estão explícitos porque o padrão do Flink é generoso: com esses valores o cluster inteiro fica em torno de 4,5 GB e cabe em qualquer máquina.</p>
<p>Antes de submeter qualquer coisa, a API REST diz se o cluster está inteiro de forma mais direta que a UI:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s localhost:8081/overview</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;taskmanagers&#34;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nt">&#34;slots-total&#34;</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span> <span class="nt">&#34;slots-available&#34;</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span> <span class="nt">&#34;flink-version&#34;</span><span class="p">:</span> <span class="s2">&#34;2.3.0&#34;</span><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Os dois TaskManagers registrados e os quatro slots livres são a confirmação de que o cluster formou. A imagem ainda traz jobs de exemplo, então dá para ver um grafo em execução sem escrever uma linha:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker <span class="nb">exec</span> jobmanager ./bin/flink run -d <span class="se">\
</span></span></span><span class="line"><span class="cl">  /opt/flink/examples/streaming/TopSpeedWindowing.jar</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Com ele rodando, <code>slots-available</code> cai para 3 e a UI finalmente tem o que mostrar.</p>
<p>A Web UI foi onde eu mais aprendi. Ela mostra o grafo do job, o paralelismo real de cada operador, o histórico de checkpoints (duração e tamanho) e, principalmente, o <strong>backpressure</strong>: quando um operador não consegue acompanhar o ritmo do anterior e a pressão se propaga para trás até a fonte. Ver isso acontecendo em tempo real ensina mais do que qualquer diagrama.</p>
<p>Para SQL, o caminho mais curto é o cliente interativo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker <span class="nb">exec</span> -it jobmanager ./bin/sql-client.sh</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h2>Deploy<span class="hx:absolute hx:-mt-20" id="deploy"></span>
    <a href="#deploy" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O <code>docker-compose.yml</code> acima é um laboratório. Ele sobe um cluster, mas esconde quase todas as decisões que aparecem quando o job precisa rodar por meses sem alguém olhando. Essa foi a parte que mais me obrigou a ler manual, porque aqui não tem intuição que salve: é conta, é limite de container e é escolha de onde o estado vai parar.</p>
<h3>Modos de execução<span class="hx:absolute hx:-mt-20" id="modos-de-execução"></span>
    <a href="#modos-de-execu%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Antes de escolher onde rodar, é preciso escolher como. O Flink tem dois modos vivos hoje, e a diferença entre eles é o ciclo de vida do cluster.</p>
<p>No <strong>session mode</strong>, o cluster sobe primeiro e fica de pé esperando. Você submete vários jobs para o mesmo JobManager, e todos disputam os mesmos TaskManagers. É o que o meu compose faz, e é o modo certo para exploração, para o cliente SQL e para jobs curtos que não compensam o custo de subir um cluster novo. O preço é a ausência de isolamento: um job que estoura a memória derruba o TaskManager, e junto com ele vão as tarefas de todos os outros jobs que tinham slots naquele processo.</p>
<p>No <strong>application mode</strong>, cada aplicação ganha o seu próprio cluster dedicado, que nasce com ela e morre com ela. A diferença mais importante é sutil e está no <code>main()</code>: em vez de rodar na máquina de quem submete, ele roda dentro do JobManager. Isso tira do cliente a responsabilidade de baixar dependências e montar o grafo do job, e é o que torna o modo adequado para automação, porque não existe mais um processo cliente de fora que precisa continuar vivo. Para produção, é a resposta padrão.</p>
<p>Existiu ainda um terceiro modo, o per-job, que também dava um cluster por job mas mantinha o <code>main()</code> no cliente. Ele foi descontinuado e não faz mais parte da linha 2.x. Se você encontrar <code>-t yarn-per-job</code> em algum tutorial, é material de uma versão anterior.</p>
<p>Ortogonal a isso está o gerenciador de recursos: standalone (você mesmo sobe os processos, que é o caso do Docker Compose), Kubernetes ou YARN. A combinação que vejo recomendada com mais frequência hoje é application mode sobre Kubernetes.</p>
<h3>Kubernetes<span class="hx:absolute hx:-mt-20" id="kubernetes"></span>
    <a href="#kubernetes" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>No Kubernetes existem três caminhos, e vale entender a diferença porque eles resolvem problemas distintos.</p>
<p>O primeiro é <strong>standalone no Kubernetes</strong>: você escreve dois <code>Deployment</code>, um para o JobManager e outro para o TaskManager, um <code>Service</code> e um <code>ConfigMap</code> com o <code>flink-conf.yaml</code>. O Flink não sabe que está no Kubernetes, ele só vê processos que se registram. Funciona, e é transparente, mas a escala é sua: mudar paralelismo é mudar réplicas na mão, e fazer upgrade com savepoint é uma sequência de comandos que alguém precisa lembrar de executar na ordem certa.</p>
<p>O segundo é o <strong>native Kubernetes</strong>, em que o próprio JobManager fala com a API do Kubernetes e cria os pods de TaskManager que faltam para atender o paralelismo pedido. Você submete com <code>flink run-application -t kubernetes-application</code> e não escreve manifesto de TaskManager nenhum. O cluster passa a se dimensionar de acordo com o job, e não o contrário.</p>
<p>O terceiro, e o que eu usaria, é o <strong>Flink Kubernetes Operator</strong>. Ele instala CRDs e transforma o job em um objeto declarativo do cluster:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">flink.apache.org/v1beta1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">FlinkDeployment</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">otlp-processor</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">registry.example.com/otlp-processor:1.4.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">flinkVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v2_0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">serviceAccount</span><span class="p">:</span><span class="w"> </span><span class="l">flink</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">flinkConfiguration</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">taskmanager.numberOfTaskSlots</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;4&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">state.backend.type</span><span class="p">:</span><span class="w"> </span><span class="l">rocksdb</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">state.checkpoints.dir</span><span class="p">:</span><span class="w"> </span><span class="l">s3://flink-state/otlp-processor/checkpoints</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">state.savepoints.dir</span><span class="p">:</span><span class="w"> </span><span class="l">s3://flink-state/otlp-processor/savepoints</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">execution.checkpointing.interval</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;60s&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">high-availability.type</span><span class="p">:</span><span class="w"> </span><span class="l">kubernetes</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">high-availability.storageDir</span><span class="p">:</span><span class="w"> </span><span class="l">s3://flink-state/otlp-processor/ha</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">jobManager</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">resource</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">cpu</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">memory</span><span class="p">:</span><span class="w"> </span><span class="l">2048m</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">taskManager</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">resource</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">cpu</span><span class="p">:</span><span class="w"> </span><span class="m">4</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">memory</span><span class="p">:</span><span class="w"> </span><span class="l">8192m</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">job</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">jarURI</span><span class="p">:</span><span class="w"> </span><span class="l">local:///opt/flink/usrlib/otlp-processor.jar</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">parallelism</span><span class="p">:</span><span class="w"> </span><span class="m">12</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">upgradeMode</span><span class="p">:</span><span class="w"> </span><span class="l">savepoint</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O que esse manifesto compra é operação. O <code>upgradeMode: savepoint</code> significa que, quando eu trocar a tag da imagem e aplicar de novo, o operator dispara um savepoint, para o job, sobe a versão nova e restaura o estado a partir dele, sem que ninguém precise executar <code>flink stop --savepointPath</code> manualmente às três da manhã. Ele também cuida de rollback quando o job novo não estabiliza, e traz um autoscaler que observa o backpressure real dos operadores e ajusta o paralelismo, que é bem diferente de um HPA olhando uso de CPU do pod.</p>
<p>Dois detalhes que não são opcionais. O primeiro é a <code>serviceAccount</code>: o JobManager precisa de permissão de RBAC para criar pods e para ler e escrever ConfigMaps, porque é em ConfigMap que a alta disponibilidade nativa guarda quem é o líder e qual é o último checkpoint. O segundo é o <code>high-availability.storageDir</code>: o ConfigMap guarda só o ponteiro, os metadados de verdade vão para armazenamento durável. Sem esse diretório configurado, um JobManager que morre volta sem saber de onde retomar, e o estado inteiro se perde apesar dos checkpoints existirem.</p>
<h3>Memória<span class="hx:absolute hx:-mt-20" id="memória"></span>
    <a href="#mem%c3%b3ria" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Essa é a conta que mais dá trabalho, porque o Flink não tem um número de memória, tem uma árvore deles. O valor que você configura é o <code>taskmanager.memory.process.size</code>, e ele é o total que o processo pode ocupar no sistema operacional. Tudo o mais é subdivisão.</p>
<p>Dentro dele, o Flink separa primeiro o que pertence à JVM e não ao Flink: o metaspace (<code>taskmanager.memory.jvm-metaspace.size</code>, 256m por padrão) e um overhead para pilhas de thread, buffers internos e código nativo (<code>taskmanager.memory.jvm-overhead.fraction</code>, 10% do process size). O que sobra é a memória do Flink propriamente dita, e ela se divide em quatro partes:</p>
<ul>
<li>O heap de tarefa, onde vivem os seus objetos e o estado quando o backend é o de heap.</li>
<li>A memória gerenciada, que é off-heap e é onde o RocksDB aloca seus block caches e write buffers. Em job batch, é também onde acontecem sort e hash join.</li>
<li>A memória de rede, que são os buffers de troca de dados entre tarefas.</li>
<li>Um pedaço reservado para o framework, que raramente se mexe.</li>
</ul>
<p>A regra que importa acima de todas: <strong>o <code>process.size</code> tem que caber no limite de memória do container</strong>. Se você der 8Gi de limite no pod e configurar <code>process.size: 8192m</code>, o pod vai ser morto por OOMKill, porque o cgroup conta também páginas de arquivo, alocações nativas do RocksDB fora do que o Flink contabiliza e o que o próprio kernel usa. Deixar uma folga de 10 a 15% resolve. Para 8Gi de limite, eu configuraria em torno de 7000m e dormiria melhor.</p>
<p>A segunda decisão é como dividir. Se o estado é grande e o backend é RocksDB, a memória gerenciada é a que faz diferença, porque é ela que determina o tamanho do cache antes de o acesso virar leitura de disco. Uma divisão de partida razoável é 40% de memória gerenciada e o resto entre heap e rede. Se o estado é pequeno e cabe em heap, o caminho inverso: <code>taskmanager.memory.managed.fraction: 0.1</code> e todo o resto para o heap.</p>
<p>A memória de rede é a que mais surpreende. Ela é 10% da memória do Flink por padrão, limitada a 1 GB, e o consumo cresce com o número de conexões entre tarefas, que por sua vez cresce com o quadrado do paralelismo em trocas do tipo <code>keyBy</code>. Um job com paralelismo 4 que passa para 200 pode começar a falhar com erro de buffers insuficientes sem que uma linha do código tenha mudado. É um dos poucos casos em que aumentar o paralelismo piora as coisas antes de melhorar.</p>
<h3>CPU e slots<span class="hx:absolute hx:-mt-20" id="cpu-e-slots"></span>
    <a href="#cpu-e-slots" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Slot não é CPU. Um slot é uma fatia de memória e um lugar no escalonamento, mas as threads de todos os slots de um TaskManager competem pelos mesmos cores da JVM. Não há isolamento de CPU entre slots.</p>
<p>Por isso a regra de partida é simples: <code>taskmanager.numberOfTaskSlots</code> igual ao número de cores que o container tem de limite. Quatro cores, quatro slots. Ir muito acima disso não aumenta a vazão, só aumenta a troca de contexto e o tempo de pausa que o coletor de lixo tem que administrar.</p>
<p>O paralelismo total do job então é o número de TaskManagers vezes os slots de cada um, e essa multiplicação é o número que aparece no manifesto. Três TaskManagers de quatro slots dão doze, que é o paralelismo do exemplo acima.</p>
<p>Para chegar ao número certo de TaskManagers, eu não tentaria calcular por primeiros princípios. O caminho que faz sentido é medir: subir o job com paralelismo baixo, injetar carga conhecida e olhar <code>numRecordsInPerSecond</code> por subtarefa na UI ou no Prometheus. Isso dá uma vazão por slot. Divide o pico esperado por esse número, adiciona uma folga de uns 30% para picos e para reprocessamento (porque depois de uma queda o job precisa correr mais rápido do que a produção para alcançar o presente) e o resultado é quantos slots são necessários. Um job de filtro e projeção pode fazer centenas de milhares de eventos por segundo em um slot; um job com janela grande, estado em RocksDB e deserialização de JSON pode fazer alguns milhares. A diferença entre esses dois casos é de ordens de grandeza, e é exatamente por isso que não dá para adivinhar.</p>
<p>Vale lembrar que mudar o paralelismo de um job com estado depois não é livre: o estado é redistribuído entre as novas subtarefas na restauração, e o teto continua sendo o <code>maxParallelism</code> fixado na primeira execução, pelo motivo que descrevi na seção de paralelismo.</p>
<h3>Disco local<span class="hx:absolute hx:-mt-20" id="disco-local"></span>
    <a href="#disco-local" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Com o RocksDB, o estado de trabalho não fica em memória, fica em arquivos no disco local do TaskManager, apontado por <code>io.tmp.dirs</code>. Isso é o que permite ter estado maior que a RAM, e é também o que faz o tipo de disco importar.</p>
<p>Precisa ser SSD local, não volume de rede. O RocksDB é uma LSM tree: escreve em arquivos ordenados e depois compacta esses arquivos em segundo plano, reescrevendo dados que já estavam lá. Essa amplificação de escrita, somada a leituras aleatórias a cada acesso de estado, é exatamente o padrão de I/O que um disco de rede atende mal. Já vi o mesmo job ir de estável a inutilizável só pela troca do tipo de volume.</p>
<p>Para o tamanho, a conta parte do estado lógico: número de chaves ativas vezes o tamanho do estado por chave, dividido pelo paralelismo, dá o estado por TaskManager. Depois disso, multiplique. Duas a três vezes é uma margem razoável, porque a compactação precisa de espaço para escrever os arquivos novos antes de apagar os antigos, e porque os checkpoints incrementais mantêm arquivos referenciados por snapshots anteriores que ainda não foram descartados. Dez milhões de chaves com 1 KB de estado cada dão 10 GB de estado lógico; em três TaskManagers, isso é algo entre 7 e 10 GB de disco em cada um.</p>
<p>No Kubernetes, isso significa escolher entre <code>emptyDir</code> e volume persistente. Com <code>emptyDir</code>, o disco morre com o pod, e um restart obriga o TaskManager a baixar o estado inteiro do armazenamento remoto antes de voltar a processar, o que em estados grandes é a diferença entre segundos e muitos minutos de recuperação. Com um volume persistente e <code>state.backend.local-recovery: true</code>, a cópia local sobrevive ao restart e a recuperação lê do disco em vez da rede. É mais complexidade em troca de tempo de recuperação, e a escolha depende de quanto o atraso custa.</p>
<h3>S3 e o sistema de arquivos remoto<span class="hx:absolute hx:-mt-20" id="s3-e-o-sistema-de-arquivos-remoto"></span>
    <a href="#s3-e-o-sistema-de-arquivos-remoto" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Aqui está a confusão que eu mesmo tinha antes de estudar: <strong>o S3 não é onde o estado vive, é onde as cópias dele são guardadas</strong>. O estado quente está no heap ou no disco local do TaskManager, e é lá que ele é lido e escrito a cada evento. O S3 entra em quatro momentos, e todos eles são periódicos ou excepcionais.</p>
<p>O primeiro são os checkpoints. A cada intervalo configurado, cada operador escreve seu estado em <code>state.checkpoints.dir</code>. É isso que sobrevive à perda simultânea de todos os pods.</p>
<p>O segundo são os savepoints, em <code>state.savepoints.dir</code>, que é o mesmo mecanismo com outro ciclo de vida: eles são seus, não são apagados pelo Flink, e é deles que sai o upgrade sem perder estado.</p>
<p>O terceiro são os metadados de alta disponibilidade, em <code>high-availability.storageDir</code>. O ConfigMap do Kubernetes é pequeno demais para guardar o grafo do job e os ponteiros completos, então ele guarda apenas a referência e o conteúdo fica no armazenamento remoto.</p>
<p>O quarto, quando existe, são os próprios dados: uma fonte que lê Parquet de um bucket, ou um sink que escreve resultados particionados por hora.</p>
<p>A razão de ser S3, e não um disco compartilhado, é que a durabilidade precisa ser independente do cluster. Um checkpoint que está no mesmo hardware que o job não protege contra a perda desse hardware. O armazenamento de objetos é barato, replicado e continua existindo depois de o cluster inteiro deixar de existir, que é precisamente a propriedade que faz um job de streaming ser recuperável. GCS, Azure Blob e HDFS ocupam o mesmo lugar na arquitetura, com outra letra no esquema da URI.</p>
<p>A configuração tem uma pegadinha que custa tempo. O Flink traz duas implementações de S3, e elas não são intercambiáveis. A <code>flink-s3-fs-presto</code> é a indicada para checkpoints, porque é rápida com arquivos pequenos e não depende de renomear objetos, operação que o S3 não tem de forma atômica e que emula com uma cópia seguida de uma remoção. A <code>flink-s3-fs-hadoop</code> é a que suporta escrita recuperável, que é o que o sink de arquivos precisa para fazer upload multipart e confirmar só no checkpoint. Muita gente instala as duas e usa <code>s3p://</code> para o estado e <code>s3a://</code> para os dados. As duas são plugins e precisam ir para <code>/opt/flink/plugins/</code>, cada uma em seu próprio diretório, e não para <code>lib/</code>. No <code>lib/</code> elas carregam no classloader principal e conflitam entre si.</p>
<p>O último ponto é sobre o intervalo de checkpoint, que parece uma configuração de latência e é também uma conta de custo. Cada checkpoint escreve pelo menos um arquivo por subtarefa com estado. Um job com paralelismo 200 e checkpoint a cada 10 segundos produz na ordem de setenta mil objetos por hora, e o S3 cobra por requisição além de limitar a taxa por prefixo. Isso aparece como latência de checkpoint subindo e, no limite, como checkpoint expirando por timeout. Duas defesas ajudam: subir o <code>state.storage.fs.memory-threshold</code> para que estados pequenos viajem dentro do arquivo de metadados em vez de virarem objetos separados, e aceitar um intervalo maior. Um minuto entre checkpoints é um valor comum, e o que ele custa é apenas a quantidade de trabalho que precisa ser refeita depois de uma falha.</p>
<h2>O ecossistema em volta<span class="hx:absolute hx:-mt-20" id="o-ecossistema-em-volta"></span>
    <a href="#o-ecossistema-em-volta" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O Flink raramente aparece sozinho. Ele é um motor de processamento, e um motor precisa de algo que alimente, de algum lugar onde depositar e de gente que leia o que ele produziu. Boa parte do trabalho de montar um pipeline é escolher essas peças, então vale mapear as que mais aparecem ao lado dele e, principalmente, por que aparecem.</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">Ferramenta</th>
          <th style="text-align: left">Onde entra</th>
          <th style="text-align: left">Por que ela aparece junto</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><strong>Apache Kafka</strong></td>
          <td style="text-align: left">Ingestão</td>
          <td style="text-align: left">Desacopla quem produz de quem processa e guarda o histórico. É o que permite reprocessar um job desde o começo, e não apenas a partir de agora</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Debezium</strong></td>
          <td style="text-align: left">Ingestão</td>
          <td style="text-align: left">Ler binlog e WAL corretamente, com snapshot inicial e sem perder transação, é difícil o bastante para ninguém querer reescrever. O Flink CDC o usa por baixo</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Schema Registry, com Avro ou Protobuf</strong></td>
          <td style="text-align: left">Contrato</td>
          <td style="text-align: left">Um job que desserializa JSON na mão quebra na primeira mudança do produtor. O registro torna o contrato explícito e verificável antes do deploy, não em produção</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Apache Iceberg</strong></td>
          <td style="text-align: left">Armazenamento</td>
          <td style="text-align: left">Dá transação, evolução de schema e viagem no tempo sobre arquivos em object storage. Sem isso o job escreve Parquet solto e quem lê enxerga escrita pela metade</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Apache Paimon</strong></td>
          <td style="text-align: left">Armazenamento</td>
          <td style="text-align: left">Nasceu dentro da comunidade do Flink para o caso que o Iceberg atende pior: atualização por chave primária em alta taxa, com changelog que dá para ler de volta</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Apache Hudi</strong></td>
          <td style="text-align: left">Armazenamento</td>
          <td style="text-align: left">A terceira opção do mesmo espaço, mais antiga, com foco em upsert e leitura incremental</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Hive Metastore, AWS Glue ou Nessie</strong></td>
          <td style="text-align: left">Catálogo</td>
          <td style="text-align: left">O formato de tabela sabe quais arquivos formam uma tabela. O catálogo sabe quais tabelas existem. Sem ele, não há o que listar no Flink SQL</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Trino</strong></td>
          <td style="text-align: left">Consulta</td>
          <td style="text-align: left">O Flink não é motor de consulta interativa. O Trino lê as mesmas tabelas Iceberg para responder a pergunta que ninguém tinha previsto</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>ClickHouse, Druid ou Pinot</strong></td>
          <td style="text-align: left">Serving analítico</td>
          <td style="text-align: left">Dashboard quer resposta em milissegundos sobre dados recentes. O Flink pré-agrega e empurra para lá, e a consulta do usuário nunca toca o stream</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>PostgreSQL e MySQL</strong></td>
          <td style="text-align: left">Operacional</td>
          <td style="text-align: left">Fonte via CDC de um lado e destino do resultado agregado do outro, quando quem consome é uma aplicação e não um analista</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Redis</strong></td>
          <td style="text-align: left">Serving de baixa latência</td>
          <td style="text-align: left">Guarda o que já foi calculado para quem precisa ler fora do stream, em microssegundos. É também o online store típico de uma feature store</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Feast</strong></td>
          <td style="text-align: left">Machine learning</td>
          <td style="text-align: left">Feature store. Garante que a feature usada no treino e a usada na inferência sejam a mesma coisa, calculada do mesmo jeito</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Prometheus e Grafana</strong></td>
          <td style="text-align: left">Operação</td>
          <td style="text-align: left">Métricas do próprio job (duração de checkpoint, backpressure, lag por partição) e destino natural das métricas derivadas que o job calcula</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>Airflow ou Dagster</strong></td>
          <td style="text-align: left">Orquestração</td>
          <td style="text-align: left">Um job de streaming não tem agenda, mas tudo em volta dele tem: backfill, compactação de tabela, expiração de snapshot, upgrade com savepoint</td>
      </tr>
  </tbody>
</table>
<p>Três dessas escolhas têm um detalhe que só aparece depois, e vale adiantar.</p>
<p>O primeiro é o problema dos arquivos pequenos. O sink do Iceberg confirma o que escreveu quando o checkpoint fecha, então o intervalo de checkpoint deixa de ser só uma decisão de recuperação e passa a determinar quantos arquivos e quantos snapshots a tabela ganha por hora. Checkpoint a cada dez segundos em um job com paralelismo alto produz uma tabela com milhares de arquivos minúsculos por dia, que é o pior formato possível para quem for lê-la depois. A solução não é evitar o Iceberg, é aceitar que compactação e expiração de snapshot são trabalho recorrente, e é justamente esse trabalho que costuma justificar um Airflow ao lado de um pipeline que, no papel, não tinha nada de agendado.</p>
<p>O segundo é o catálogo, que quase sempre é lembrado tarde. É fácil pensar no formato de tabela e esquecer que alguém precisa saber que a tabela existe. Sem catálogo configurado, o <code>CREATE TABLE</code> do Flink SQL vive só na sessão e some quando o cliente fecha, e o Trino do outro lado não enxerga nada do que foi criado. Escolher o catálogo é o que faz Flink e os outros motores estarem falando da mesma tabela, em vez de dos mesmos arquivos por coincidência.</p>
<p>O terceiro é a razão de existir uma feature store, que demorei a entender. O problema que ela resolve não é de armazenamento, é de consistência: se a feature &ldquo;média de compras nos últimos 30 minutos&rdquo; é calculada por um job Flink na inferência e por uma query SQL no treino, as duas vão discordar em algum detalhe, e o modelo passa a ver em produção dados diferentes dos que viu ao aprender. Manter uma definição só, alimentada pelo mesmo job, é o ponto. E vale notar que parte disso está migrando para dentro do próprio Flink com as funções de modelo da 2.1 e 2.2, que descrevo no post sobre o Flink 2.3.</p>
<h2>Complexidades<span class="hx:absolute hx:-mt-20" id="complexidades"></span>
    <a href="#complexidades" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>Ajuste fino de RocksDB e o comportamento do estado quando ele cresce muito além da memória.</li>
<li>Estratégias de join entre streams, principalmente interval joins e temporal joins.</li>
<li>A biblioteca de CEP, citada lá em cima nos casos de uso e ainda não testada por mim.</li>
<li>Como isso tudo se comporta de verdade em produção sob carga irregular, a parte que nenhuma documentação ensina.</li>
</ul>
<h2>O que ficou<span class="hx:absolute hx:-mt-20" id="o-que-ficou"></span>
    <a href="#o-que-ficou" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Terminei a semana com uma impressão que não esperava ter: a parte difícil de streaming não é distribuir o processamento. Isso o Flink resolve. A parte difícil é o <strong>tempo</strong>: aceitar que os dados chegam desordenados, que &ldquo;completo&rdquo; é uma aposta e não um fato, e que quase toda decisão técnica aqui é, no fundo, uma escolha entre latência e correção.</p>
<p>Escrever isto me forçou a admitir umas três ou quatro coisas que eu achava que sabia e não sabia. Valeu pelo esforço.</p>
<p>Falta dizer por que fui atrás disso, porque não é Flink pelo Flink. O que eu quero é processar OTLP, o formato que o OpenTelemetry Collector fala, e na prática isso significa os quatro sinais: logs, spans, métricas e profiles.</p>
<p>O Collector recebe, transforma e encaminha muito bem, mas ele é feito para o dado passar. No momento em que a pergunta precisa de memória entre eventos, de janela por event time ou de correlacionar sinais que chegam separados e fora de ordem, ela cai exatamente no que este texto descreve: <code>keyBy</code>, estado por chave e watermark. É esse encaixe que eu quero testar na próxima rodada.</p>
<p>Próxima semana no cronograma: revisar o roadmap de Data Engineer. Continuo escrevendo enquanto aprendo.</p>
]]></content:encoded><category>apache-flink</category><category>data-engineering</category><category>aprendendo-em-publico</category></item><item><title>Mito de Er</title><link>https://apolzek.github.io/2026/08/04/mito-de-er/</link><guid isPermaLink="true">https://apolzek.github.io/2026/08/04/mito-de-er/</guid><pubDate>Tue, 04 Aug 2026 12:00:00 GMT</pubDate><description>&lt;p&gt;Este é um texto sobre o Mito de Er, da República de Platão. É um livro que li sem possuir, naquele momento, os fundamentos necessários para compreendê-lo plenamente. Ainda assim, sua leitura se fez necessária para a fase da vida pela qual eu estava passando. Antes de lê-lo, pesquisei sobre a obra, já conhecia a biografia de Platão e havia despertado meu interesse pela filosofia.&lt;/p&gt;
&lt;p&gt;O que é a &lt;em&gt;República&lt;/em&gt; ? É uma das obras mais importantes e conhecidas de Platão, escrita provavelmente no século IV a.C., por volta de 375 a.C. A obra é construída principalmente na forma de diálogos, tendo Sócrates como personagem central.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Este é um texto sobre o Mito de Er, da República de Platão. É um livro que li sem possuir, naquele momento, os fundamentos necessários para compreendê-lo plenamente. Ainda assim, sua leitura se fez necessária para a fase da vida pela qual eu estava passando. Antes de lê-lo, pesquisei sobre a obra, já conhecia a biografia de Platão e havia despertado meu interesse pela filosofia.</p>
<p>O que é a <em>República</em> ? É uma das obras mais importantes e conhecidas de Platão, escrita provavelmente no século IV a.C., por volta de 375 a.C. A obra é construída principalmente na forma de diálogos, tendo Sócrates como personagem central.</p>
<p>Um dos grandes problemas que orientam a discussão é uma pergunta aparentemente simples, mas bastante incômoda: <em>vale a pena ser justo ?</em> O que é a justiça ? É melhor ser justo por si mesma ou apenas pelas vantagens e recompensas que ela pode trazer ? Em outras palavras: vale a pena agir corretamente quando a injustiça poderia trazer mais benefícios ?</p>
<p>A partir dessas questões, Platão desenvolve uma longa investigação sobre a justiça, a educação, a organização da cidade, a natureza da alma e o modo como devemos viver.</p>
<p>É nesse contexto que aparece o <em>Mito de Er</em>, encerrando a obra. O mito narra a experiência de Er, um homem que teria morrido em batalha e retornado à vida para contar o que viu após a morte. Seu relato aborda o destino das almas, as consequências de suas escolhas e a possibilidade de escolher uma nova vida.</p>
<p>Por estar no final da <em>República</em>, o <em>Mito de Er</em> funciona como uma conclusão simbólica da obra e retoma uma de suas questões fundamentais: quais são as consequências de nossas escolhas e que tipo de vida devemos escolher ? Ele não deve ser entendido simplesmente como &ldquo;a resposta final&rdquo; de Platão, mas como uma narrativa que encerra e amplia a investigação filosófica desenvolvida ao longo do diálogo.</p>
<p><strong>Quem conta a história ?</strong> Sócrates, que foi o professor de Platão e é o personagem principal de quase todos os seus livros. É pela boca dele que o mito é narrado.</p>
<h2>O cenário<span class="hx:absolute hx:-mt-20" id="o-cenário"></span>
    <a href="#o-cen%c3%a1rio" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Er é um guerreiro que morre em batalha. O corpo dele fica dias no campo sem apodrecer e, na hora de ser cremado, ele acorda e  conta tudo o que viu do outro lado.</p>
<p>Basicamente: o primeiro relato de experiência de quase-morte da história da filosofia.</p>
<p>O que Er descreve é o processo pelo qual as almas retornam ao mundo sensível. Traduzindo o termo técnico: &ldquo;mundo sensível&rdquo; é simplesmente o mundo que a gente enxerga, toca, cheira, o mundo material, O DAQUI. Platão acreditava que a alma existe antes e depois do corpo, e que ela vai e volta. O que Er viu foi justamente o balcão de embarque dessa volta. E lá tem gente trabalhando. As Moiras, três figuras da mitologia grega que controlam o destino de cada pessoa, representadas como fiandeiras (mulheres que fiam, ou seja, produzem linha). A ideia é que a sua vida é um fio, e elas cuidam desse fio:</p>
<ul>
<li><strong>Cloto</strong>:  a que <em>fia</em>, ou seja, produz o fio da sua vida.</li>
<li><strong>Láquesis</strong>: a que <em>mede</em> e distribui, sorteia e organiza os destinos.</li>
<li><strong>Átropos</strong>: a que <em>corta</em> o fio. Ela é a morte. O nome dela significa literalmente &ldquo;a inflexível&rdquo;, a que não volta atrás.</li>
</ul>
<p>Guarde esse detalhe, porque ele importa: as Moiras estão lá, o destino existe na história. Platão não está negando o destino. Ele vai fazer algo mais esperto do que isso.</p>
<h2>As regras do jogo<span class="hx:absolute hx:-mt-20" id="as-regras-do-jogo"></span>
    <a href="#as-regras-do-jogo" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Agora vem a parte boa. O renascimento das almas segue regras bem específicas.. e cada uma delas carrega um recado.</p>
<h3>1. Acontece um sorteio<span class="hx:absolute hx:-mt-20" id="1-acontece-um-sorteio"></span>
    <a href="#1-acontece-um-sorteio" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Quando as almas estão prontas pra renascer, elas participam de um sorteio. Só que o sorteio não define a vida que você vai ter. Ele define apenas a ordem em que cada alma vai escolher. É senha de fila, não prêmio. Você não sorteia o que vai levar; você sorteia quando vai poder pegar.</p>
<h3>2. Sobra vida pra todo mundo<span class="hx:absolute hx:-mt-20" id="2-sobra-vida-pra-todo-mundo"></span>
    <a href="#2-sobra-vida-pra-todo-mundo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>E aqui está o ponto central do mito: há mais opções de vida do que almas. O acaso participa do processo. O sorteio existe e determina a ordem em que cada alma escolherá, mas a escolha da vida que cada uma levará é, em última instância, sua. E, junto com a escolha, vem também a responsabilidade por ela. Essa ideia me atingiu de uma forma inesperada. Foi o tipo de passagem que me fez parar a leitura e ficar olhando para o teto por um bom tempo.</p>
<h3>3. Os deuses não têm culpa<span class="hx:absolute hx:-mt-20" id="3-os-deuses-não-têm-culpa"></span>
    <a href="#3-os-deuses-n%c3%a3o-t%c3%aam-culpa" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Platão bate nessa tecla com força total. A frase que aparece no mito é praticamente uma sentença: a culpa é de quem escolhe, e a divindade não tem culpa nenhuma. Ninguém empurrou nada goela abaixo de ninguém. Cada alma escolhe seu caminho conforme a sabedoria que tem ou conforme a falta dela. Isso, no contexto grego, era uma provocação e tanto. A cultura da época estava cheia de histórias em que os deuses arruinavam a vida das pessoas por capricho, ciúme ou vingança. Platão está cortando essa desculpa pela raiz.</p>
<h2>O cardápio de vidas<span class="hx:absolute hx:-mt-20" id="o-cardápio-de-vidas"></span>
    <a href="#o-card%c3%a1pio-de-vidas" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>E o que estava disponível pra escolher ? De tudo. Vidas ricas, vidas pobres, vidas de tirano, vidas de gente famosa, vidas de anônimo, vidas simples, vidas de bicho até. Um bufê completo de existências possíveis. Só que é aí que a coisa complica de verdade. Porque a vida que parece boa nem sempre é boa. A vida de tirano brilha de longe: poder, dinheiro, todo mundo te obedecendo. De perto, é uma máquina de produzir injustiça e, segundo Platão, quem produz injustiça acaba destruindo a si mesmo por dentro. No mito, tem uma cena ótima: uma alma se atira em cima da vida de tirano, agarra ela com tudo, e só depois percebe o que veio junto no pacote. Aí sai se lamentando e culpando os deuses. Mas ela mesma escolheu. Ela só não olhou direito. Então o desafio real nunca foi escolher a vida mais chamativa. O desafio era ter autoconhecimento suficiente pra escolher uma vida que levasse à virtude e não à injustiça.</p>
<p>Rapidinho sobre esses dois termos, porque eles são o coração do texto:</p>
<ul>
<li><strong>Virtude</strong>, pra Platão, não é ser &ldquo;bonzinho&rdquo; nem seguir regras de etiqueta moral. É a alma funcionar bem, em ordem, com a razão no comando, como um instrumento afinado.</li>
<li><strong>Injustiça</strong> é o contrário: a alma bagunçada, desgovernada, com os impulsos mandando. Pra Platão, isso é uma espécie de doença da alma. E é por isso que ser injusto é ruim pra você, mesmo que ninguém descubra e mesmo que você lucre com isso.</li>
</ul>
<h3>E tem um detalhe cruel: o caráter<span class="hx:absolute hx:-mt-20" id="e-tem-um-detalhe-cruel-o-caráter"></span>
    <a href="#e-tem-um-detalhe-cruel-o-car%c3%a1ter" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O <strong>caráter</strong> o jeito de ser, a estrutura moral da pessoa é muito difícil de mudar depois que a escolha é feita. Ou seja: você não escolhe só as circunstâncias da sua vida. Você escolhe também, junto e embutido, o tipo de pessoa que vai ser dentro dela. Vem tudo no mesmo pacote. Por isso o mito insiste tanto que a habilidade mais importante do mundo é conseguir prever o bem e o mal que uma vida pode trazer. Saber olhar pra uma opção e enxergar não o brilho da superfície, mas o que ela vai fazer com você por dentro, a longo prazo. Sem isso, você está apostando no escuro com o próprio destino.</p>
<h2>E por que a gente não lembra de nada disso?<span class="hx:absolute hx:-mt-20" id="e-por-que-a-gente-não-lembra-de-nada-disso"></span>
    <a href="#e-por-que-a-gente-n%c3%a3o-lembra-de-nada-disso" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Boa pergunta, o mito responde. Antes de reencarnar, as almas atravessam uma planície árida e bebem a água do <strong>Rio do Esquecimento</strong> (em grego, <em>Lete</em>, palavra que significa literalmente &ldquo;esquecimento&rdquo;). Um gole e pronto: apagou tudo. Por isso ninguém chega aqui lembrando da própria escolha. A gente nasce sem o manual, sem o recibo, sem a memória de ter assinado embaixo. O que é meio sacana, admito. Mas também é exatamente o que torna a vida uma vida de verdade, e não a execução de um roteiro que você já leu antes.</p>
<h2>O que Platão realmente quis dizer com tudo isso<span class="hx:absolute hx:-mt-20" id="o-que-platão-realmente-quis-dizer-com-tudo-isso"></span>
    <a href="#o-que-plat%c3%a3o-realmente-quis-dizer-com-tudo-isso" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>1. É um ataque frontal ao fatalismo<span class="hx:absolute hx:-mt-20" id="1-é-um-ataque-frontal-ao-fatalismo"></span>
    <a href="#1-%c3%a9-um-ataque-frontal-ao-fatalismo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>Fatalismo</strong> é a crença de que está tudo escrito e não adianta nada fazer nada, &ldquo;se tem que ser, vai ser&rdquo;. O primo dele, o <strong>determinismo</strong>, é a ideia de que tudo o que você faz já foi causado por coisas fora do seu controle: genética, criação, sociedade, acaso. Repare no que Platão faz: ele <strong>não nega</strong> que exista destino. As Moiras estão lá, tecendo. O sorteio existe. O acaso existe e interfere de verdade. Ele poderia simplesmente ter dito &ldquo;destino não existe&rdquo;, mas não disse. O que ele faz é mais sofisticado: ele mostra que, <strong>mesmo dentro do destino, sobra liberdade</strong>. Você não escolhe a posição na fila. Você escolhe o que faz com ela. E como sempre sobra opção boa na mesa, a desculpa &ldquo;eu não tinha alternativa&rdquo; simplesmente não cola.</p>
<p>Essa é a tese central: <strong>o ser humano é livre e, por ser livre, é responsável pelo caminho que trilha</strong>.</p>
<h3>2. É um recado sobre de quem é a culpa<span class="hx:absolute hx:-mt-20" id="2-é-um-recado-sobre-de-quem-é-a-culpa"></span>
    <a href="#2-%c3%a9-um-recado-sobre-de-quem-%c3%a9-a-culpa" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Toda a estrutura do mito serve pra fechar as saídas de emergência que a gente usa pra fugir da responsabilidade.</p>
<p>Culpar os deuses ?? Negado.. a divindade é inocente. Culpar o sorteio ?? Negado.. sobrava vida boa. Culpar a falta de opção ?? Negado.. o cardápio era enorme. Culpar quem escolheu antes de você ?? Negado.. pergunte ao Ulisses.</p>
<p>Sobra o quê ?? Sobra você =)))))</p>
<h3>3. A &ldquo;escolha&rdquo; não aconteceu num além-vida, ela está acontecendo agora<span class="hx:absolute hx:-mt-20" id="3-a-escolha-não-aconteceu-num-além-vida-ela-está-acontecendo-agora"></span>
    <a href="#3-a-escolha-n%c3%a3o-aconteceu-num-al%c3%a9m-vida-ela-est%c3%a1-acontecendo-agora" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Esse é o ponto mais importante, e é onde o mito deixa de ser uma história sobre reencarnação e vira uma história sobre <strong>hoje</strong>. Lembra que o caráter vem embutido na escolha e é quase impossível de mudar depois ? Lembra que a alma que agarrou a vida de tirano fez isso por impulso, sem examinar ? Então: essa é uma imagem do que a gente faz o tempo todo.</p>
<p>A cada decisão, o que estudar, com quem andar, o que aceitar, o que deixar passar, que tipo de pessoa se permitir ser numa discussão, você está de novo naquele campo, escolhendo um pacote de vida sem enxergar direito o que vem dentro. E, assim como no mito, depois de um tempo fica muito difícil desfazer. O Rio do Esquecimento é a imagem perfeita disso: a gente esquece que está escolhendo. Age no automático, culpa as circunstâncias, e só percebe o pacote quando já é tarde.</p>
<h3>4. Por isso a filosofia importa.. e essa é a real conclusão do livro<span class="hx:absolute hx:-mt-20" id="4-por-isso-a-filosofia-importa-e-essa-é-a-real-conclusão-do-livro"></span>
    <a href="#4-por-isso-a-filosofia-importa-e-essa-%c3%a9-a-real-conclus%c3%a3o-do-livro" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Aqui tudo se encaixa. Se a única coisa que separa uma escolha boa de uma escolha desastrosa é a <strong>capacidade de examinar as opções antes de agarrar a primeira que brilha</strong>, então a habilidade mais valiosa que existe é aprender a examinar. É isso que Platão chama de filosofia. Não decoreba de nomes gregos: é o treino de olhar pras coisas com calma e enxergar o que elas realmente são, e não o que aparentam ser. É exatamente o que Ulisses fez enquanto todo mundo se atropelava.</p>
<p>E aí a <em>República</em> fecha o círculo. A pergunta inicial era &ldquo;vale a pena ser justo ?&rdquo;. A resposta do mito é: <strong>vale</strong>, porque a justiça não é uma regra imposta de fora que te atrapalha, é o que mantém sua alma em ordem, e uma alma em ordem é a única coisa que consegue escolher bem. Ser injusto não te dá vantagem; te deixa cego na hora que mais importa enxergar.</p>
<hr>
<p>&ldquo;Sobre tudo o que se deve guardar, guarda o teu coração, porque dele procedem as fontes da vida.&rdquo; — Provérbios 4:23</p>
]]></content:encoded><category>filosofia</category><category>leitura</category></item><item><title>Diálogo sobre o Mestre e a Prática</title><link>https://apolzek.github.io/2026/07/15/dialogo-sobre-o-mestre-e-a-pratica/</link><guid isPermaLink="true">https://apolzek.github.io/2026/07/15/dialogo-sobre-o-mestre-e-a-pratica/</guid><pubDate>Wed, 15 Jul 2026 12:00:00 GMT</pubDate><description>&lt;p&gt;&lt;em&gt;Cenário: Sócrates caminha próximo à palestra quando Glauco sai dela às pressas, ainda com poeira nos ombros e visivelmente contrariado.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SÓCRATES:&lt;/strong&gt; Glauco, sais da palestra como quem foge de um incêndio. Que aconteceu ?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GLAUCO:&lt;/strong&gt; Uma discussão, Sócrates. Os jovens zombaram do velho Íkos, que ali ensina os lutadores. Diziam: &amp;ldquo;como pode ensinar a vencer quem nunca venceu em Olímpia ?&amp;rdquo;. E confesso que ri com eles, mas agora algo me incomoda.&lt;/p&gt;</description><content:encoded><![CDATA[<p><em>Cenário: Sócrates caminha próximo à palestra quando Glauco sai dela às pressas, ainda com poeira nos ombros e visivelmente contrariado.</em></p>
<p><strong>SÓCRATES:</strong> Glauco, sais da palestra como quem foge de um incêndio. Que aconteceu ?</p>
<p><strong>GLAUCO:</strong> Uma discussão, Sócrates. Os jovens zombaram do velho Íkos, que ali ensina os lutadores. Diziam: &ldquo;como pode ensinar a vencer quem nunca venceu em Olímpia ?&rdquo;. E confesso que ri com eles, mas agora algo me incomoda.</p>
<p><strong>SÓCRATES:</strong> O que te incomoda ?</p>
<p><strong>GLAUCO:</strong> Íkos treinou três campeões. Nenhum de seus discípulos jamais perguntou por suas coroas, e todos saíram coroados. Como pode isso ?</p>
<p><strong>SÓCRATES:</strong> Diz-me: o auriga que vence a corrida, vence pelos seus olhos ou pelos olhos do cavalo ?</p>
<p><strong>GLAUCO:</strong> Pelos seus, suponho.</p>
<p><strong>SÓCRATES:</strong> E aquele que, da arquibancada, percebe que o auriga aperta a curva cedo demais e perde meio corpo de vantagem, precisa ele próprio saber conduzir o carro ?</p>
<p><strong>GLAUCO:</strong> Talvez não. Ver o erro e cometer o acerto parecem coisas distintas.</p>
<p><strong>SÓCRATES:</strong> Então já encontraste duas artes onde julgavas haver uma. Há a arte de fazer, que mora nas mãos e nas pernas, e há a arte de ver e explicar, que mora nos olhos e na palavra. O corredor mais veloz de Atenas saberia dizer-te <em>por que</em> é veloz ?</p>
<p><strong>GLAUCO:</strong> Duvido. Perguntei certa vez a um deles e respondeu apenas: &ldquo;corro desde menino&rdquo;.</p>
<p><strong>SÓCRATES:</strong> Eis um homem que sabe e não sabe que sabe. Se o puseres a ensinar, dirá aos alunos &ldquo;correi desde meninos&rdquo;, e nada terá ensinado. E Íkos, que talvez jamais tenha derrubado um adversário, viu cair dez mil homens e sabe dizer de cada queda a causa. Qual dos dois formará o campeão ?</p>
<p><strong>GLAUCO:</strong> Íkos, ao que parece. Então está resolvido: quem ensina não precisa ser exímio no que ensina.</p>
<p><strong>SÓCRATES:</strong> Não corras tão depressa, Glauco, ou tropeçarás como o auriga da curva. Responde-me: aceitarias aprender a nadar com um homem que nunca entrou na água ?</p>
<p><strong>GLAUCO:</strong> De modo algum!</p>
<p><strong>SÓCRATES:</strong> E por quê, se há pouco concordamos que ensinar e fazer são artes distintas ?</p>
<p><strong>GLAUCO:</strong> Porque&hellip; esse homem não conheceria a água. Falaria do mar como quem fala de um país que nunca visitou.</p>
<p><strong>SÓCRATES:</strong> Muito bem. Então distingue comigo: uma coisa é exigir do mestre que seja o <em>melhor</em> dos praticantes; outra é exigir que <em>conheça de dentro</em> aquilo que ensina. Íkos lutou, caiu, sentiu a areia na boca. Não colheu coroas, mas colheu o conhecimento da luta. O que ele não tem é a glória; o que ele tem é a ciência.</p>
<p><strong>GLAUCO:</strong> Mas então por que exigimos as coroas, Sócrates ? Por que a primeira pergunta que fazemos ao mestre é &ldquo;o que venceste ?&rdquo;, e não &ldquo;o que compreendes ?&rdquo;.</p>
<p><strong>SÓCRATES:</strong> Porque as coroas se contam com os dedos, e a compreensão não se conta. O homem apressado prefere a prova que os olhos verificam num instante à prova que exige convivência e tempo. A coroa é um rótulo pendurado no peito, e já conversamos, noutra ocasião, sobre o quanto os rótulos poupam o trabalho de pensar.</p>
<p><strong>GLAUCO:</strong> Então erramos ao cobrar reputação prática de quem ensina ?</p>
<p><strong>SÓCRATES:</strong> Erramos e acertamos, Glauco, e é isso que te peço para não simplificar. Acertamos, porque a reputação prática é um indício: quem fez, provavelmente conhece. Erramos, porque tomamos o indício por prova: quem fez pode não saber explicar, e quem explica pode ter conhecido a coisa sem jamais brilhar nela. A coroa prova que o homem venceu; não prova que ele sabe por que venceu, e menos ainda que saiba fazer <em>outro</em> vencer.</p>
<p><strong>GLAUCO:</strong> E o contrário também vale ? A ausência de coroa não prova ignorância ?</p>
<p><strong>SÓCRATES:</strong> Assim como a ausência de riqueza não prova preguiça, nem a ausência de barba prova juventude. Mas nota: também não prova sabedoria. Há mestres sem coroa que são como Íkos, e há mestres sem coroa que são como o homem seco que ensina a nadar. A ausência do rótulo apenas te devolve o trabalho que o rótulo prometia poupar: o de examinar.</p>
<p><strong>GLAUCO:</strong> E como examino, Sócrates ? Se não pelas vitórias do mestre, pelo quê ?</p>
<p><strong>SÓCRATES:</strong> Pelos frutos que são propriamente seus. Os frutos do lutador são as quedas que provoca; os frutos do mestre são os lutadores que forma. Pergunta ao mestre não &ldquo;o que venceste ?&rdquo;, mas &ldquo;quem aprendeu contigo, e o que se tornou ?&rdquo;. E pergunta a ti mesmo, ao ouvi-lo: &ldquo;compreendo mais agora do que antes ?&rdquo;. Se a resposta for sim, tens diante de ti um mestre, tenha ele cem coroas ou nenhuma.</p>
<p><strong>GLAUCO:</strong> Ainda assim, algo em mim resiste. Quando o mestre também executa com excelência, não é ele melhor ?</p>
<p><strong>SÓCRATES:</strong> Talvez seja mais raro, Glauco, como é raro o cavalo que é belo e veloz ao mesmo tempo. Quando encontrares um homem que faz com excelência e explica com clareza, honra-o, pois a natureza foi generosa duas vezes. Mas não faças da generosidade dupla a medida de todos: exigirias de cada cavalo que fosse o mais belo <em>e</em> o mais veloz, e ficarias a pé.</p>
<p><strong>GLAUCO:</strong> Então, afinal, é certo ou errado cobrar de quem ensina a reputação de quem executa ?</p>
<p><strong>SÓCRATES:</strong> Essa balança, Glauco, não a carrego eu; carrega-a cada um que escolhe a quem confiar seu aprendizado. Eu apenas te mostro os pesos: de um lado, o indício honesto que a prática dá; do outro, a injustiça de medir uma arte com a régua de outra. <strong>Pesa-os tu mesmo, a cada mestre que encontrares, e desconfia de quem te vender a resposta pronta, inclusive de mim, que nunca venci luta alguma e passo os dias ensinando na praça</strong>.</p>
<p>..então Glauco ficou em silêncio, Sócrates sorriu do próprio exemplo..</p>
<h2>Glossário<span class="hx:absolute hx:-mt-20" id="glossário"></span>
    <a href="#gloss%c3%a1rio" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li><em>palestra</em>: escola de luta e exercícios físicos na Grécia antiga</li>
<li><em>auriga</em>: condutor de carros de corrida puxados por cavalos</li>
<li><em>Olímpia</em>: santuário onde se realizavam os Jogos Olímpicos da antiguidade</li>
<li><em>Íkos</em>: inspirado em Íkos de Tarento, atleta e depois célebre treinador grego</li>
</ul>
<h2>Nota<span class="hx:absolute hx:-mt-20" id="nota"></span>
    <a href="#nota" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quem ensina precisa dominar, de maneira prática, aquilo que ensina? A resposta parece óbvia, mas talvez não seja. Costumamos exigir de quem ensina o currículo de quem executa, como se a capacidade de explicar fosse consequência inevitável da experiência. No entanto, há quem nunca tenha alcançado os maiores feitos e ainda assim ensine com clareza e profundidade. Da mesma forma, há praticantes extraordinários incapazes de transmitir o que fazem.</p>
<p>Talvez a experiência prática seja um forte indício de competência, mas não uma prova definitiva da capacidade de ensinar. E a ausência dessa experiência também não basta, por si só, para invalidar um bom mestre. Entre a prática e a didática existe uma relação, mas não uma identidade. A questão permanece aberta: o que, afinal, qualifica alguém para ensinar ? Talvez a única certeza seja a lição socrática de que indícios não bastam e de que examinar cada caso continua sendo um trabalho sem atalhos.</p>
]]></content:encoded><category>filosofia</category><category>dialogos</category></item><item><title>Velhas Armadilhas, Novas Vítimas</title><link>https://apolzek.github.io/2026/07/04/velhas-armadilhas-novas-vitimas/</link><guid isPermaLink="true">https://apolzek.github.io/2026/07/04/velhas-armadilhas-novas-vitimas/</guid><pubDate>Sat, 04 Jul 2026 12:00:00 GMT</pubDate><description>&lt;p&gt;Em um mundo onde há excesso de informação, o maior desafio já não é encontrar conteúdo, mas &lt;strong&gt;separar o que realmente agrega valor do que apenas prende sua atenção&lt;/strong&gt;. Neste artigo, compartilho estratégias práticas para evitar perder horas com vídeos e materiais que parecem educativos, mas entregam pouco conhecimento de fato, uma situação muito comum no YouTube, que hoje é uma das minhas principais fontes de estudo.&lt;/p&gt;
&lt;h2&gt;Sem lenga lenga&lt;span class="hx:absolute hx:-mt-20" id="sem-lenga-lenga"&gt;&lt;/span&gt;
&lt;a href="#sem-lenga-lenga" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Cuidado com uma armadilha cada vez mais comum nos vídeos que você assiste: a enrolação calculada. Ela tem até nomes técnicos. Padding é o ato de inflar um vídeo com conteúdo desnecessário apenas para aumentar sua duração. Burying the lede é esconder a informação principal até o final, fazendo você consumir muito conteúdo antes de chegar ao que realmente importa. Watch time baiting é prender sua atenção pelo maior tempo possível para aumentar as métricas de retenção. Fique atento aos sinais: promessas constantemente adiadas, recapitulações desnecessárias e a sensação de que a resposta está sempre &amp;ldquo;logo ali&amp;rdquo;. Quando perceber isso, lembre-se: o vídeo não foi feito para te informar, mas para maximizar o tempo que você passa assistindo. &lt;strong&gt;Seu tempo é valioso&lt;/strong&gt;. Decida rapidamente se vale a pena continuar assistindo. Se o conteúdo demonstrar valor, consuma-o por completo. Se perceber que está sendo apenas retido, saia sem culpa.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Em um mundo onde há excesso de informação, o maior desafio já não é encontrar conteúdo, mas <strong>separar o que realmente agrega valor do que apenas prende sua atenção</strong>. Neste artigo, compartilho estratégias práticas para evitar perder horas com vídeos e materiais que parecem educativos, mas entregam pouco conhecimento de fato, uma situação muito comum no YouTube, que hoje é uma das minhas principais fontes de estudo.</p>
<h2>Sem lenga lenga<span class="hx:absolute hx:-mt-20" id="sem-lenga-lenga"></span>
    <a href="#sem-lenga-lenga" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Cuidado com uma armadilha cada vez mais comum nos vídeos que você assiste: a enrolação calculada. Ela tem até nomes técnicos. Padding é o ato de inflar um vídeo com conteúdo desnecessário apenas para aumentar sua duração. Burying the lede é esconder a informação principal até o final, fazendo você consumir muito conteúdo antes de chegar ao que realmente importa. Watch time baiting é prender sua atenção pelo maior tempo possível para aumentar as métricas de retenção. Fique atento aos sinais: promessas constantemente adiadas, recapitulações desnecessárias e a sensação de que a resposta está sempre &ldquo;logo ali&rdquo;. Quando perceber isso, lembre-se: o vídeo não foi feito para te informar, mas para maximizar o tempo que você passa assistindo. <strong>Seu tempo é valioso</strong>. Decida rapidamente se vale a pena continuar assistindo. Se o conteúdo demonstrar valor, consuma-o por completo. Se perceber que está sendo apenas retido, saia sem culpa.</p>
<h2>Clickbait<span class="hx:absolute hx:-mt-20" id="clickbait"></span>
    <a href="#clickbait" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Tome cuidado com títulos feitos para explorar sua curiosidade e seu medo de ficar de fora. Frases como &ldquo;você precisa saber isso&rdquo;, &ldquo;sem isso você nunca vai conseguir&rdquo;, &ldquo;o erro que 99% das pessoas cometem&rdquo; ou &ldquo;ninguém está te contando isso&rdquo; costumam apelar mais para a emoção do que para o conteúdo. Um bom conteúdo explica claramente o que você vai aprender. Um clickbait faz promessas vagas, exageradas ou urgentes para conquistar seu clique antes de conquistar sua confiança. Antes de abrir um vídeo, pergunte-se: o título descreve uma informação concreta ou apenas desperta ansiedade e curiosidade? Se a promessa for genérica demais, trate com ceticismo. Seu clique e sua atenção têm valor. Gaste-os apenas com conteúdos que entregam o que prometem.</p>
<h2>Eu acho..<span class="hx:absolute hx:-mt-20" id="eu-acho"></span>
    <a href="#eu-acho" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Tome cuidado com conteúdos que apresentam opiniões pessoais como se fossem fatos. Nem todo criador está transmitindo conhecimento; muitas vezes, está compartilhando sua própria visão de mundo, suas experiências e suas crenças, sem deixar isso explícito. Conhecimento pode ser verificado, questionado e comparado com evidências. Opiniões refletem interpretações individuais e podem variar de pessoa para pessoa. Quando essa diferença não é clara, é fácil absorver uma perspectiva particular como se fosse uma verdade universal. Pergunte-se: essa afirmação está sendo sustentada por dados, evidências ou fontes confiáveis, ou é apenas uma conclusão do autor? Desenvolver esse hábito ajuda a aprender de forma mais crítica e a construir suas próprias conclusões, em vez de apenas adotar as de outra pessoa.</p>
<h2>Produto perfeito (:<span class="hx:absolute hx:-mt-20" id="produto-perfeito-"></span>
    <a href="#produto-perfeito-" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Lembre-se de que apresentação não é conteúdo. Produção impecável, animações, slides chamativos, edição acelerada e promessas grandiosas podem tornar uma mensagem mais convincente, mas não a tornam mais verdadeira ou mais útil. Avalie primeiro o que está sendo dito, não como está sendo apresentado. A forma pode chamar sua atenção; o conteúdo é o que deve justificar seu tempo e sua confiança.</p>
<h2>Sofistas do Youtube<span class="hx:absolute hx:-mt-20" id="sofistas-do-youtube"></span>
    <a href="#sofistas-do-youtube" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>usam títulos e miniaturas chamativos para parecerem especialistas;</li>
<li>falam com muita confiança, mesmo quando o conteúdo é superficial;</li>
<li>priorizam retenção de audiência e engajamento em vez de profundidade;</li>
<li>vendem a sensação de aprendizado, mas entregam pouco conhecimento prático.</li>
</ul>
]]></content:encoded><category>estudo</category><category>produtividade</category></item><item><title>As 20 empresas mais valiosas do mundo (junho de 2026)</title><link>https://apolzek.github.io/2026/06/06/most-valuable-companies-top20/</link><guid isPermaLink="true">https://apolzek.github.io/2026/06/06/most-valuable-companies-top20/</guid><pubDate>Sat, 06 Jun 2026 12:00:00 GMT</pubDate><description>&lt;p&gt;Um retrato do momento..&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Empresa&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;th&gt;Categoria (o que faz)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;NVIDIA&lt;/td&gt;
&lt;td&gt;~US$ 5,0 tri&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#chips&lt;/code&gt; &lt;code&gt;#IA&lt;/code&gt; &lt;code&gt;#GPU&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Alphabet (Google)&lt;/td&gt;
&lt;td&gt;~US$ 4,4 tri&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#busca&lt;/code&gt; &lt;code&gt;#publicidade&lt;/code&gt; &lt;code&gt;#IA&lt;/code&gt; &lt;code&gt;#cloud&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Apple&lt;/td&gt;
&lt;td&gt;~US$ 4,3 tri&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#eletrônicos&lt;/code&gt; &lt;code&gt;#iPhone&lt;/code&gt; &lt;code&gt;#serviços&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Microsoft&lt;/td&gt;
&lt;td&gt;~US$ 2,9 tri&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#software&lt;/code&gt; &lt;code&gt;#cloud&lt;/code&gt; &lt;code&gt;#IA&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;SpaceX&lt;/td&gt;
&lt;td&gt;~US$ 2,65 tri&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#aeroespacial&lt;/code&gt; &lt;code&gt;#satélites&lt;/code&gt; &lt;code&gt;#IA&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Amazon&lt;/td&gt;
&lt;td&gt;~US$ 2,59 tri&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#ecommerce&lt;/code&gt; &lt;code&gt;#cloud&lt;/code&gt; &lt;code&gt;#logística&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;TSMC&lt;/td&gt;
&lt;td&gt;~US$ 2,2 tri&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#chips&lt;/code&gt; &lt;code&gt;#semicondutores&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Broadcom&lt;/td&gt;
&lt;td&gt;~US$ 1,86 tri&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#chips&lt;/code&gt; &lt;code&gt;#IA&lt;/code&gt; &lt;code&gt;#semicondutores&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Saudi Aramco&lt;/td&gt;
&lt;td&gt;~US$ 1,7 tri&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#petróleo&lt;/code&gt; &lt;code&gt;#energia&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Meta&lt;/td&gt;
&lt;td&gt;~US$ 1,45 tri&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#redessociais&lt;/code&gt; &lt;code&gt;#publicidade&lt;/code&gt; &lt;code&gt;#IA&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Tesla&lt;/td&gt;
&lt;td&gt;~US$ 1,44 tri&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#carroselétricos&lt;/code&gt; &lt;code&gt;#energia&lt;/code&gt; &lt;code&gt;#IA&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Micron&lt;/td&gt;
&lt;td&gt;~US$ 1,12 tri&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#chips&lt;/code&gt; &lt;code&gt;#memória&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;Berkshire Hathaway&lt;/td&gt;
&lt;td&gt;~US$ 1,06 tri&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#investimentos&lt;/code&gt; &lt;code&gt;#seguros&lt;/code&gt; &lt;code&gt;#holding&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;Eli Lilly&lt;/td&gt;
&lt;td&gt;~US$ 1,02 tri&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#farmacêutica&lt;/code&gt; &lt;code&gt;#saúde&lt;/code&gt; &lt;code&gt;#emagrecimento&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;Walmart&lt;/td&gt;
&lt;td&gt;~US$ 968 bi&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#varejo&lt;/code&gt; &lt;code&gt;#supermercado&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;JPMorgan Chase&lt;/td&gt;
&lt;td&gt;~US$ 896 bi&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#banco&lt;/code&gt; &lt;code&gt;#finanças&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;AMD&lt;/td&gt;
&lt;td&gt;~US$ 844 bi&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#chips&lt;/code&gt; &lt;code&gt;#processadores&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;ASML&lt;/td&gt;
&lt;td&gt;~US$ 719 bi&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#litografia&lt;/code&gt; &lt;code&gt;#equipamentos&lt;/code&gt; &lt;code&gt;#chips&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;Intel&lt;/td&gt;
&lt;td&gt;~US$ 633 bi&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#chips&lt;/code&gt; &lt;code&gt;#processadores&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;Visa&lt;/td&gt;
&lt;td&gt;~US$ 623 bi&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#pagamentos&lt;/code&gt; &lt;code&gt;#finanças&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;O valor de mercado de uma empresa é menos uma verdade absoluta e mais um retrato da percepção do mercado num dado momento.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Um retrato do momento..</p>
<table>
  <thead>
      <tr>
          <th>#</th>
          <th>Empresa</th>
          <th>Valor</th>
          <th>Categoria (o que faz)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1</td>
          <td>NVIDIA</td>
          <td>~US$ 5,0 tri</td>
          <td><code>#chips</code> <code>#IA</code> <code>#GPU</code></td>
      </tr>
      <tr>
          <td>2</td>
          <td>Alphabet (Google)</td>
          <td>~US$ 4,4 tri</td>
          <td><code>#busca</code> <code>#publicidade</code> <code>#IA</code> <code>#cloud</code></td>
      </tr>
      <tr>
          <td>3</td>
          <td>Apple</td>
          <td>~US$ 4,3 tri</td>
          <td><code>#eletrônicos</code> <code>#iPhone</code> <code>#serviços</code></td>
      </tr>
      <tr>
          <td>4</td>
          <td>Microsoft</td>
          <td>~US$ 2,9 tri</td>
          <td><code>#software</code> <code>#cloud</code> <code>#IA</code></td>
      </tr>
      <tr>
          <td>5</td>
          <td>SpaceX</td>
          <td>~US$ 2,65 tri</td>
          <td><code>#aeroespacial</code> <code>#satélites</code> <code>#IA</code></td>
      </tr>
      <tr>
          <td>6</td>
          <td>Amazon</td>
          <td>~US$ 2,59 tri</td>
          <td><code>#ecommerce</code> <code>#cloud</code> <code>#logística</code></td>
      </tr>
      <tr>
          <td>7</td>
          <td>TSMC</td>
          <td>~US$ 2,2 tri</td>
          <td><code>#chips</code> <code>#semicondutores</code></td>
      </tr>
      <tr>
          <td>8</td>
          <td>Broadcom</td>
          <td>~US$ 1,86 tri</td>
          <td><code>#chips</code> <code>#IA</code> <code>#semicondutores</code></td>
      </tr>
      <tr>
          <td>9</td>
          <td>Saudi Aramco</td>
          <td>~US$ 1,7 tri</td>
          <td><code>#petróleo</code> <code>#energia</code></td>
      </tr>
      <tr>
          <td>10</td>
          <td>Meta</td>
          <td>~US$ 1,45 tri</td>
          <td><code>#redessociais</code> <code>#publicidade</code> <code>#IA</code></td>
      </tr>
      <tr>
          <td>11</td>
          <td>Tesla</td>
          <td>~US$ 1,44 tri</td>
          <td><code>#carroselétricos</code> <code>#energia</code> <code>#IA</code></td>
      </tr>
      <tr>
          <td>12</td>
          <td>Micron</td>
          <td>~US$ 1,12 tri</td>
          <td><code>#chips</code> <code>#memória</code></td>
      </tr>
      <tr>
          <td>13</td>
          <td>Berkshire Hathaway</td>
          <td>~US$ 1,06 tri</td>
          <td><code>#investimentos</code> <code>#seguros</code> <code>#holding</code></td>
      </tr>
      <tr>
          <td>14</td>
          <td>Eli Lilly</td>
          <td>~US$ 1,02 tri</td>
          <td><code>#farmacêutica</code> <code>#saúde</code> <code>#emagrecimento</code></td>
      </tr>
      <tr>
          <td>15</td>
          <td>Walmart</td>
          <td>~US$ 968 bi</td>
          <td><code>#varejo</code> <code>#supermercado</code></td>
      </tr>
      <tr>
          <td>16</td>
          <td>JPMorgan Chase</td>
          <td>~US$ 896 bi</td>
          <td><code>#banco</code> <code>#finanças</code></td>
      </tr>
      <tr>
          <td>17</td>
          <td>AMD</td>
          <td>~US$ 844 bi</td>
          <td><code>#chips</code> <code>#processadores</code></td>
      </tr>
      <tr>
          <td>18</td>
          <td>ASML</td>
          <td>~US$ 719 bi</td>
          <td><code>#litografia</code> <code>#equipamentos</code> <code>#chips</code></td>
      </tr>
      <tr>
          <td>19</td>
          <td>Intel</td>
          <td>~US$ 633 bi</td>
          <td><code>#chips</code> <code>#processadores</code></td>
      </tr>
      <tr>
          <td>20</td>
          <td>Visa</td>
          <td>~US$ 623 bi</td>
          <td><code>#pagamentos</code> <code>#finanças</code></td>
      </tr>
  </tbody>
</table>
<blockquote>
  <p>O valor de mercado de uma empresa é menos uma verdade absoluta e mais um retrato da percepção do mercado num dado momento.</p>

</blockquote>
]]></content:encoded><category>negocios</category><category>economia</category></item><item><title>O estudo como caminho de libertação</title><link>https://apolzek.github.io/2026/05/09/o-estudo-como-caminho-de-libertacao/</link><guid isPermaLink="true">https://apolzek.github.io/2026/05/09/o-estudo-como-caminho-de-libertacao/</guid><pubDate>Sat, 09 May 2026 12:00:00 GMT</pubDate><description>&lt;h2&gt;Reflexões de um ex-péssimo aluno&lt;span class="hx:absolute hx:-mt-20" id="reflexões-de-um-ex-péssimo-aluno"&gt;&lt;/span&gt;
&lt;a href="#reflex%c3%b5es-de-um-ex-p%c3%a9ssimo-aluno" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Quando alguém decide estudar seriamente um assunto novo, quase sempre experimenta uma espécie de ansiedade intelectual. É um fenômeno compreensível. À medida que cresce o desejo de conhecer, cresce também a consciência da própria ignorância. Quanto mais se enxerga, mais se percebe aquilo que ainda permanece oculto. &amp;ldquo;Só sei que nada sei&amp;rdquo; — Sócrates.&lt;/p&gt;
&lt;p&gt;Nesse momento surge um problema que antes não existia: a limitação do tempo. Já não basta desejar aprender; torna-se necessário discernir o que merece aprofundamento e o que pode permanecer no campo da informação geral. A maturidade intelectual consiste precisamente nisso: saber até onde é preciso ir e onde convém parar.&lt;/p&gt;</description><content:encoded><![CDATA[<h2>Reflexões de um ex-péssimo aluno<span class="hx:absolute hx:-mt-20" id="reflexões-de-um-ex-péssimo-aluno"></span>
    <a href="#reflex%c3%b5es-de-um-ex-p%c3%a9ssimo-aluno" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quando alguém decide estudar seriamente um assunto novo, quase sempre experimenta uma espécie de ansiedade intelectual. É um fenômeno compreensível. À medida que cresce o desejo de conhecer, cresce também a consciência da própria ignorância. Quanto mais se enxerga, mais se percebe aquilo que ainda permanece oculto. &ldquo;Só sei que nada sei&rdquo; — Sócrates.</p>
<p>Nesse momento surge um problema que antes não existia: a limitação do tempo. Já não basta desejar aprender; torna-se necessário discernir o que merece aprofundamento e o que pode permanecer no campo da informação geral. A maturidade intelectual consiste precisamente nisso: saber até onde é preciso ir e onde convém parar.</p>
<p>Observando a realidade, percebe-se que existem, em linhas gerais, três tipos de estudiosos. O primeiro é o generalista superficial: conhece um pouco de muitas coisas, mas raramente alcança a essência de qualquer uma delas. O segundo é o especialista restrito: domina profundamente poucos assuntos, sacrificando a amplitude em favor da profundidade. O terceiro reúne ambas as qualidades: possui vasta cultura e, ao mesmo tempo, conhecimento profundo dos temas que estudou. É o tipo mais raro, pois exige não apenas inteligência, mas disciplina, tempo e uma extraordinária capacidade de ordenar o próprio pensamento.</p>
<p>O problema é que esse terceiro ideal, embora admirável, torna-se praticamente inalcançável dentro do tempo médio de vida de um ser humano, sobretudo para aqueles que possuem curiosidade genuína por diversas áreas do conhecimento. Basta um exame sincero da realidade para perceber que não há tempo suficiente para absorver tudo o que a humanidade já produziu, muito menos para acompanhar o que continua produzindo.</p>
<p>É nesse ponto que surge o primeiro grande desafio intelectual: a delimitação. Traçar uma linha não é um ato de resignação, mas de prudência. Sem essa delimitação, o estudante corre o risco de viver permanentemente assombrado pela sensação de insuficiência, como se estivesse sempre atrasado diante da imensidão do conhecimento disponível. A definição clara de objetivos não empobrece a busca intelectual; ao contrário, confere-lhe direção e sentido.</p>
<p>Há, porém, um segundo problema igualmente importante. A humanidade produziu e continua produzindo uma quantidade colossal de conteúdo sobre praticamente todos os assuntos imagináveis. Entretanto, quantidade jamais foi sinônimo de qualidade. Grande parte dessa produção nasce da ingenuidade, da falta de rigor, da insuficiência de informações ou, pior ainda, da substituição dos fatos por opiniões travestidas de conhecimento. Nem todo autor merece confiança. Nem toda obra merece atenção.</p>
<p>Por isso, duas virtudes tornam-se indispensáveis ao estudante: a capacidade de delimitar o próprio campo de investigação e a capacidade de selecionar cuidadosamente suas fontes. Antes de iniciar uma jornada de estudos, reserve algum tempo para determinar o que realmente precisa aprender e qual nível de domínio é necessário para atingir seu objetivo. Ao alcançá-lo, considere a meta cumprida. Caso contrário, o estudo transforma-se numa busca interminável, incapaz de produzir qualquer sensação legítima de conclusão.</p>
<p>Da mesma forma, reserve tempo para identificar autores, professores e pensadores que tenham resistido ao julgamento do tempo e cuja reputação tenha sido construída sobre a seriedade intelectual. Prefira aqueles que demonstram prudência ao avaliar seus predecessores, que conhecem a tradição da qual falam e que possuem a honestidade de distinguir claramente entre aquilo que sabem, aquilo em que acreditam e aquilo que apenas supõem. <strong>Em matéria de estudo, escolher os guias corretos é, muitas vezes, mais importante do que escolher os temas corretos.</strong></p>
<p>Todos falam sobre a importância de possuir uma base de conhecimento sólida, mas poucos realmente compreendem o que isso significa. Quando você estuda um assunto e sente que não está entendendo nada, ou quando abre um livro e já nas primeiras páginas o considera excessivamente difícil ou enfadonho, talvez o problema não esteja no assunto nem no livro. Frequentemente, falta algum fundamento anterior que tornaria aquela experiência inteligível.</p>
<p>Tente estudar Cálculo Diferencial e Integral sem antes dominar a álgebra básica. Eu vivi essa experiência e posso afirmar com segurança: não funciona. <strong>O intelecto não avança por saltos arbitrários.</strong> O conhecimento possui uma estrutura, e cada degrau pressupõe os anteriores.</p>
<p>Demorei muito tempo para compreender a importância da lógica, da linguagem, da matemática básica, da epistemologia, do método científico, da estatística e da história das ideias. Durante anos pensei: &ldquo;Para que vou precisar disso ?&rdquo;. E, sob certo aspecto, eu tinha razão. No mercado de trabalho e na vida cotidiana convencional, grande parte desses conhecimentos parece dispensável. Na maioria das profissões, é perfeitamente possível viver sem eles.</p>
<p>Mas existe uma diferença entre aquilo que é útil para ganhar a vida e aquilo que é necessário para compreender a vida. Para uma existência orientada pelo autoconhecimento e pela compreensão da realidade, esses estudos constituem fundamentos indispensáveis. São instrumentos de libertação intelectual.</p>
<p>Vale a pena perguntar a si mesmo: aquilo que estou estudando possui relação com quem eu sou ou apenas com o lugar onde trabalho ?? Está ligado à minha formação como ser humano ou apenas ao próximo objetivo profissional que desejo alcançar ?</p>
<p>Essa é uma decisão difícil. O que você aprenderá para desenvolver-se como pessoa ? E o que aprenderá para adquirir habilidades práticas, aumentar sua utilidade econômica e tornar-se mais valorizado no mercado de trabalho ? No final, ambas as dimensões são importantes. Acredito que o erro está em sacrificar completamente uma delas em favor da outra.</p>
<p>Gostaria de encerrar com uma reflexão..</p>
<p>Quantas pessoas do seu convívio você conhece que ainda carregam o título informal de &ldquo;curandeiras&rdquo;, guardiãs de saberes transmitidos de geração em geração ? Quantas existirão daqui a cem anos ? Depois de responder mentalmente, saia de casa, caminhe duas quadras e observe a realidade ao seu redor. Muito provavelmente você encontrará uma drogaria. Elas estão por toda parte: em esquinas, avenidas, centros comerciais e bairros residenciais, acessíveis a poucos minutos de distância para a maioria da população.</p>
<p>Não se trata de rejeitar os avanços da medicina nem de idealizar o passado. A questão é outra: estamos nos tornando cada vez mais dependentes de sistemas que compreendemos cada vez menos. Perdemos contato com tradições, conhecimentos locais e formas de entendimento que durante séculos fizeram parte da experiência humana.</p>
<p>Tenho a impressão de que estamos formando indivíduos cada vez mais especializados em operar mecanismos e menos capazes de compreender a si mesmos. O conhecimento cresce em volume, mas a sabedoria parece tornar-se mais rara.</p>
<p>Eu sei.. este texto começou falando sobre conhecimento e terminou com a minha indignação :<strong>/</strong> . Talvez porque, no fundo, conhecimento não seja apenas acumular informações, mas compreender o lugar que ocupamos no mundo e a direção para a qual estamos caminhando.</p>
<p>Lembro, por fim, de uma frase que ouvi certa vez de um cardiologista já falecido:</p>
<p>&ldquo;Somente o conhecimento liberta o homem.&rdquo; — E.C.</p>
<hr>
<h2>Conhecimento x Inteligência x Sabedoria<span class="hx:absolute hx:-mt-20" id="conhecimento-x-inteligência-x-sabedoria"></span>
    <a href="#conhecimento-x-intelig%c3%aancia-x-sabedoria" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><strong>Conhecimento</strong> é o conjunto de informações, conceitos e fatos que uma pessoa adquiriu por estudo, observação ou experiência. Trata-se daquilo que alguém sabe. Um médico conhece anatomia, fisiologia e farmacologia porque estudou essas disciplinas; um mecânico conhece o funcionamento de motores porque aprendeu sua estrutura e operação. O conhecimento responde principalmente à pergunta: &ldquo;o que é ?&rdquo;.</p>
<p><strong>Inteligência</strong> é a capacidade de compreender, relacionar, analisar e utilizar o conhecimento para resolver problemas ou lidar com situações novas. Duas pessoas podem possuir a mesma quantidade de conhecimento, mas apresentar níveis diferentes de inteligência ao aplicá-lo. Um estudante que aprende as fórmulas da física possui conhecimento; aquele que consegue utilizá-las para resolver um problema inédito demonstra inteligência. A inteligência responde à pergunta: &ldquo;como usar aquilo que sei ?&rdquo;.</p>
<p><strong>Sabedoria</strong> é a capacidade de julgar corretamente o valor, os limites e as consequências do conhecimento e da inteligência na vida real. É uma virtude ligada à prudência, ao discernimento e à compreensão da natureza humana. Um investidor pode ter conhecimento sobre economia e inteligência para identificar oportunidades financeiras, mas somente a sabedoria lhe permitirá perceber quando já possui o suficiente, quando o risco é excessivo ou quando a busca por dinheiro está sacrificando aspectos mais importantes da vida. A sabedoria responde à pergunta: &ldquo;vale a pena fazer isso e quais serão suas consequências ?&rdquo;.</p>
<p>Em termos simples, conhecimento é saber que o fogo queima; inteligência é entender como produzir, controlar ou utilizar o fogo; sabedoria é saber quando acendê-lo, quando apagá-lo e quando manter distância dele. O conhecimento acumula informações, a inteligência transforma informações em soluções, e a sabedoria orienta essas soluções para fins verdadeiramente bons e adequados.</p>
<p>Essa distinção aparece, sob diferentes formulações, em autores como Aristóteles, que diferenciava ciência, técnica e prudência, e também em pensadores modernos da epistemologia e da psicologia cognitiva. Em todas as tradições sérias de pensamento, a conclusão é semelhante: <em>é possível possuir muito conhecimento sem ser inteligente, ser inteligente sem ser sábio, mas é impossível ser verdadeiramente sábio sem possuir algum grau de conhecimento e inteligência. A sabedoria não substitui os dois; ela os ordena.</em></p>
<hr>
<h2>Ser polímata<span class="hx:absolute hx:-mt-20" id="ser-polímata"></span>
    <a href="#ser-pol%c3%admata" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Um <strong>polímata</strong> é alguém cuja mente transcende as fronteiras artificiais entre os campos do conhecimento, identificando os princípios, padrões e estruturas que conectam áreas distintas em uma compreensão unificada da realidade. Em vez de apenas acumular informações, o polímata sintetiza, reconstrói e integra saberes, transformando aprendizado em percepção sistêmica. Sua cognição não opera de forma linear ou compartimentalizada, mas como uma rede dinâmica de conexões, na qual ciência, filosofia, psicologia, política, finanças e experiência humana revelam diferentes expressões das mesmas verdades fundamentais.</p>
]]></content:encoded><category>filosofia</category><category>estudo</category></item><item><title>Como montar um cluster Kubernetes para hospedar seu site estático</title><link>https://apolzek.github.io/2026/05/02/kubernetes-static-website/</link><guid isPermaLink="true">https://apolzek.github.io/2026/05/02/kubernetes-static-website/</guid><pubDate>Sat, 02 May 2026 12:00:00 GMT</pubDate><description>&lt;p&gt;&lt;strong&gt;não faça isso&lt;/strong&gt;&lt;/p&gt;</description><content:encoded>&lt;p>&lt;strong>não faça isso&lt;/strong>&lt;/p>
</content:encoded><category>kubernetes</category><category>humor</category></item><item><title>Diálogo sobre os Rótulos e a Verdade</title><link>https://apolzek.github.io/2026/04/11/dialogo-sobre-rotulos-e-verdade/</link><guid isPermaLink="true">https://apolzek.github.io/2026/04/11/dialogo-sobre-rotulos-e-verdade/</guid><pubDate>Sat, 11 Apr 2026 12:00:00 GMT</pubDate><description>&lt;p&gt;&lt;em&gt;Cenário: Sócrates encontra Glauco no pórtico de uma praça, onde este examina, irritado, uma tábua cheia de inscrições.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SÓCRATES:&lt;/strong&gt; Glauco, vejo-te de cenho franzido. Que te aflige ?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GLAUCO:&lt;/strong&gt; Descobri, Sócrates, que um poeta cuja obra muito amo sustenta opiniões políticas que me repugnam. E outro, cujas ideias partilho como irmão, escreve versos sofríveis. Sinto que devo escolher um e renegar o outro.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SÓCRATES:&lt;/strong&gt; Diz-me: quando comes uma figueira, julgas o fruto pelo sabor, ou pelas opiniões do agricultor ?&lt;/p&gt;</description><content:encoded><![CDATA[<p><em>Cenário: Sócrates encontra Glauco no pórtico de uma praça, onde este examina, irritado, uma tábua cheia de inscrições.</em></p>
<p><strong>SÓCRATES:</strong> Glauco, vejo-te de cenho franzido. Que te aflige ?</p>
<p><strong>GLAUCO:</strong> Descobri, Sócrates, que um poeta cuja obra muito amo sustenta opiniões políticas que me repugnam. E outro, cujas ideias partilho como irmão, escreve versos sofríveis. Sinto que devo escolher um e renegar o outro.</p>
<p><strong>SÓCRATES:</strong> Diz-me: quando comes uma figueira, julgas o fruto pelo sabor, ou pelas opiniões do agricultor ?</p>
<p><strong>GLAUCO:</strong> Pelo sabor.</p>
<p><strong>SÓCRATES:</strong> E se o agricultor for homem perverso, mas seus figos doces, deixarás de comê-los ?</p>
<p><strong>GLAUCO:</strong> Não, pois o figo nada tem com a perversidade do homem.</p>
<p><strong>SÓCRATES:</strong> Então a obra, uma vez feita, vive por si, ou depende a cada instante de quem a fez ?</p>
<p><strong>GLAUCO:</strong> Vive por si.</p>
<p><strong>SÓCRATES:</strong> Logo, podemos julgar o figo pelo que ele é, sem nos obrigarmos a louvar o agricultor em tudo. E inversamente: o fato de o homem ser virtuoso não tornará doce um figo amargo. Mistura essas coisas, Glauco, e perderás duas vezes: perderás o gosto dos bons figos, e perderás a clareza para julgar o homem.</p>
<p><strong>GLAUCO:</strong> Compreendo. Mas há ainda outra coisa nesta tábua que me incomoda. Está escrito sobre mim: &ldquo;Glauco é tolo.&rdquo;</p>
<p><strong>SÓCRATES:</strong> Só isso ?</p>
<p><strong>GLAUCO:</strong> Só isso.</p>
<p><strong>SÓCRATES:</strong> E se eu te dissesse: &ldquo;Glauco, és tolo porque, calculando o preço de três ânforas de azeite, multiplicaste por dois e perdeste uma dracma&rdquo;, que farias ?</p>
<p><strong>GLAUCO:</strong> Veria se é verdade, e, sendo verdade, corrigiria o erro.</p>
<p><strong>SÓCRATES:</strong> E se eu apenas dissesse &ldquo;és tolo&rdquo;, sem mais ?</p>
<p><strong>GLAUCO:</strong> Não saberia o que corrigir, nem se há algo a corrigir.</p>
<p><strong>SÓCRATES:</strong> Então uma afirmação te entrega uma razão, um caminho. A outra te entrega apenas uma pedra atirada. Diz-me: confiarias teu cavalo a um homem que estende a mão pedindo as rédeas sem dizer quem é ?</p>
<p><strong>GLAUCO:</strong> Jamais.</p>
<p><strong>SÓCRATES:</strong> E o teu juízo sobre ti mesmo, coisa muito mais valiosa que um cavalo, entregarás a quem te rotula sem dizer por quê ?</p>
<p><strong>GLAUCO:</strong> Seria mais tolo do que ele me acusa de ser.</p>
<p><strong>SÓCRATES:</strong> Guarda então esta regra: a acusação sem razão é palha ao vento, ignora-a. A acusação com razão é semente que pode germinar em ti, examina-a. Mas nota bem, Glauco: ninguém pode examinar por ti. Nem eu, nem o autor da tábua, nem a multidão da praça.</p>
<p><strong>GLAUCO:</strong> Como assim, Sócrates ?</p>
<p><strong>SÓCRATES:</strong> Quero dizer que o trabalho de pesar as razões, aceitar o que se sustenta e descartar o que não se sustenta, é teu e somente teu. Não é apenas sobre homens que isto vale, Glauco. É sobre tudo o que encontras: uma lei que te impõem, um conselho que recebes, uma decisão que precisas tomar na tua casa ou na tua cidade. Em todas, alguém quererá pensar por ti.</p>
<p><strong>GLAUCO:</strong> E se eu errar ao decidir sozinho ?</p>
<p><strong>SÓCRATES:</strong> Errarás. E é isto que distingue o homem livre do escravo: o homem livre carrega as consequências do que conclui. Se decidires bem, colherás. Se decidires mal, perderás. Mas em ambos os casos terás aprendido algo que ninguém pode aprender por ti.</p>
<p><strong>GLAUCO:</strong> E aquele que prefere que outro decida em seu lugar ?</p>
<p><strong>SÓCRATES:</strong> Esse vive uma vida emprestada. Quando algo der certo, não terá o mérito. Quando algo der errado, culpará o outro, e nada aprenderá, pois o erro não foi propriamente seu. É a pior das condições, Glauco: a do homem que nunca falha por nunca ter ousado julgar.</p>
<p><strong>GLAUCO:</strong> Então a liberdade tem o preço da responsabilidade.</p>
<p><strong>SÓCRATES:</strong> Esse é seu preço, e também sua dignidade. Examina cada obra, cada acusação, cada situação. Pede sempre as razões. Decide. E quando a consequência vier, doce ou amarga, recebe-a como tua. <strong>É o único caminho para se tornar de fato alguém</strong>.</p>
<p>..então Sócrates ajeitou o manto, Glauco virou a tábua contra a parede, e ambos foram tomar uma cerveja.</p>
<h2>Glossário<span class="hx:absolute hx:-mt-20" id="glossário"></span>
    <a href="#gloss%c3%a1rio" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li><em>ânforas</em>: vasos antigos usados para guardar líquidos como vinho e azeite</li>
<li><em>dracma</em>: moeda usada na Grécia antiga</li>
<li><em>pórtico</em>: entrada com colunas na frente de um edifício</li>
<li><em>cenho franzido</em>: expressão de preocupação ou irritação; testa enrugada por tensão</li>
</ul>
<h2>Nota<span class="hx:absolute hx:-mt-20" id="nota"></span>
    <a href="#nota" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Ler os textos gregos implica não tratá-los como meras construções discursivas, mas como registros de uma percepção efetiva da realidade, orientada pela busca de apreender a essência e os processos internos dos objetos da natureza.</p>
<p>Quando li <strong>A República, de Platão</strong> eu sabia que não tinha e talvez ainda não tenha a base suficiente para aproveitá-la plenamente, mesmo assim senti que era uma leitura que valia a pena. Quero relê-la em breve, justamente para perceber como a experiência com uma obra muda conforme amadurecemos como leitores.</p>
]]></content:encoded><category>filosofia</category><category>dialogos</category></item><item><title>começando por aqui</title><link>https://apolzek.github.io/2026/04/04/apolzek/</link><guid isPermaLink="true">https://apolzek.github.io/2026/04/04/apolzek/</guid><pubDate>Sat, 04 Apr 2026 12:00:00 GMT</pubDate><description>&lt;p&gt;Reset do blog, 18 de maio de 2026&lt;/p&gt;</description><content:encoded>&lt;p>Reset do blog, 18 de maio de 2026&lt;/p>
</content:encoded><category>blog</category></item><item><title>Thoughts</title><link>https://apolzek.github.io/thoughts/</link><guid isPermaLink="true">https://apolzek.github.io/thoughts/</guid><pubDate>Mon, 01 Jan 0001 00:00:00 GMT</pubDate><description>&lt;p&gt;Compartilhando frases de terceiros e alguns pensamentos&lt;/p&gt;</description><content:encoded>&lt;p>Compartilhando frases de terceiros e alguns pensamentos&lt;/p>
</content:encoded></item><item><title>Sobre</title><link>https://apolzek.github.io/about/</link><guid isPermaLink="true">https://apolzek.github.io/about/</guid><pubDate>Mon, 01 Jan 0001 00:00:00 GMT</pubDate><description>&lt;h1&gt;Seja bem-vindo(a) ao blog!&lt;/h1&gt;&lt;p&gt;Meu nome é &lt;strong&gt;Vinícius Gomes Batista&lt;/strong&gt;. Antes de qualquer coisa, quero me apresentar brevemente e contar um pouco sobre o que você vai encontrar por aqui.&lt;/p&gt;
&lt;p&gt;Como acontece com muita gente da área de tecnologia, meu interesse por computadores começou cedo. Antes mesmo de entrar no mercado de trabalho, eu já tinha alguma experiência, prestava serviços de maneira informal e demonstrava bastante interesse por infraestrutura e segurança da informação.&lt;/p&gt;</description><content:encoded><![CDATA[<h1>Seja bem-vindo(a) ao blog!</h1><p>Meu nome é <strong>Vinícius Gomes Batista</strong>. Antes de qualquer coisa, quero me apresentar brevemente e contar um pouco sobre o que você vai encontrar por aqui.</p>
<p>Como acontece com muita gente da área de tecnologia, meu interesse por computadores começou cedo. Antes mesmo de entrar no mercado de trabalho, eu já tinha alguma experiência, prestava serviços de maneira informal e demonstrava bastante interesse por infraestrutura e segurança da informação.</p>
<p>Sou da época das lan houses. Não sou tão velho assim, mas ainda peguei os disquetes, os monitores de tubo e aqueles gabinetes brancos enormes. Três nostalgias que deixo aqui apenas como curiosidade: <em>Dracula: The Resurrection</em>, MSN e Windows XP.</p>
<p>Passei boa parte da adolescência sentado em frente ao computador, conversando com pessoas no IRC. Foi um dos ambientes onde mais aprendi. Conheci pessoas de todos os tipos, com experiências, conhecimentos e habilidades completamente diferentes. Boa parte da minha curiosidade por tecnologia foi alimentada por <em>keywords</em> que surgiam nessas conversas.</p>
<p>Minha carreira profissional começou há seis anos, como desenvolvedor fullstack, trabalhando com .NET e Vue.js. Mais tarde, migrei para Engenharia de Confiabilidade (SRE).</p>
<p>Minha transição de desenvolvedor fullstack para SRE aconteceu principalmente porque eu gostava de ter uma visão holística dos sistemas, dos produtos e até das organizações. Por onde passei, sempre quis entender tudo. Não bastava saber como uma aplicação funcionava; eu queria entender onde ela rodava, de quais componentes dependia, com quais serviços se comunicava e o que aconteceria se alguma dessas partes deixasse de funcionar.</p>
<p>Foi essa mesma curiosidade que despertou meu interesse por observabilidade. A possibilidade de representar o comportamento dos sistemas, suas dependências e integrações por meio de métricas, traces, logs e dashboards sempre me chamou a atenção. Mas, para mim, nunca se tratou apenas de colocar gráficos em uma tela. O interessante era conseguir olhar para aqueles dados e compreender o que estava acontecendo, por que estava acontecendo e como o comportamento dos sistemas mudava ao longo do tempo.</p>
<p>Com o tempo, esse interesse me levou a uma segunda transição, dessa vez para Engenharia de Plataforma, mais especificamente para plataformas de observabilidade, área em que concentrei minha atuação nos últimos 12 meses.</p>
<p>E é mais ou menos desse percurso que surge a vontade de voltar a escrever.</p>
<p>Por aqui, pretendo compartilhar um pouco do que vivi ao longo desses anos trabalhando com tecnologia. Quero falar principalmente sobre experiências práticas, problemas que enfrentei, erros que cometi, lições que me custaram caro e coisas que gostaria que alguém tivesse me contado antes.</p>
<p>Também quero abordar os desafios de trabalhar em áreas técnicas de grandes organizações. Quem já passou por esse tipo de ambiente provavelmente conhece algumas das contradições: processos excessivamente rígidos, burocracias desnecessárias e culturas organizacionais que, muitas vezes, acabam dificultando o trabalho justamente em nome da estabilidade, da segurança e da eficiência que deveriam promover.</p>
<p>A ideia não é apenas criticar essas situações, mas refletir sobre elas, entender suas causas e discutir o que poderia ser feito de maneira diferente.</p>
<p><strong>Mas não espere encontrar apenas conteúdo técnico.</strong></p>
<p>Também gosto bastante de filosofia e de refletir sobre a vida fora do trabalho. Algumas leituras, conversas e experiências acabam despertando questões que ficam martelando na minha cabeça por dias. Escrever sempre me pareceu uma boa maneira de organizar esses pensamentos.</p>
<p>Quero aproveitar este espaço para comentar livros e outras obras, compartilhar reflexões e desenvolver ideias que nem sempre estarão completamente formadas. Afinal, nem tudo o que vale a pena escrever precisa começar com uma conclusão.</p>
<p>No fim das contas, este blog é uma forma de registrar o que aprendi, organizar minhas ideias e, quem sabe, trocar experiências com outras pessoas. Não tenho a pretensão de ensinar verdades absolutas, muito menos de parecer alguém que tem respostas para tudo. Pelo contrário, muitas vezes escrevo justamente porque ainda tenho perguntas que não consegui responder.</p>
<p>E acho que é isso. Não pretendo contar toda a minha história em uma única apresentação. Se você continuar aparecendo por aqui e acompanhando meus textos, provavelmente vai acabar me conhecendo um pouco melhor com o tempo. Além disso, pretendo fazer alguns <em>appends</em> nesta página de tempos em tempos, acrescentando lembranças, experiências e pensamentos que eu considere importantes o suficiente para deixar registrados.</p>
<p>No fundo, a ideia é que, quando eu me aposentar, exista algum registro dos caminhos que percorri, das coisas que aprendi e das experiências que vivi. É algo que escrevo de mim para o meu eu do futuro, uma espécie de memória que não quero deixar se perder. Mas, quem sabe, no meio de tudo isso, alguma coisa também possa ser útil para alguém que passar por aqui.</p>
<p>Me encontra no <a href="https://github.com/apolzek"target="_blank" rel="noopener">GitHub</a> e no <a href="https://x.com/apolzek"target="_blank" rel="noopener">X</a>.</p>
<p>&ldquo;apolzek&rdquo; is a nickname born from the combination of two people I met during the IRC chat days: Apol-0 and ZekAng3l</p>
]]></content:encoded></item><item><title>Moments</title><link>https://apolzek.github.io/moments/</link><guid isPermaLink="true">https://apolzek.github.io/moments/</guid><pubDate>Mon, 01 Jan 0001 00:00:00 GMT</pubDate><description>&lt;p&gt;Fotos e legendas, meu Instagram interno&lt;/p&gt;</description><content:encoded>&lt;p>Fotos e legendas, meu Instagram interno&lt;/p>
</content:encoded></item><item><title>Cheatsheet</title><link>https://apolzek.github.io/cheatsheet/</link><guid isPermaLink="true">https://apolzek.github.io/cheatsheet/</guid><pubDate>Mon, 01 Jan 0001 00:00:00 GMT</pubDate><description>&lt;p&gt;comandos e truques para o dia a dia&lt;/p&gt;</description><content:encoded>&lt;p>comandos e truques para o dia a dia&lt;/p>
</content:encoded></item></channel></rss>