<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lucas Aguiar</title>
    <description>The latest articles on DEV Community by Lucas Aguiar (@lusqua).</description>
    <link>https://dev.to/lusqua</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1730864%2F12df57c3-3a10-43a3-a66b-71746341a576.png</url>
      <title>DEV Community: Lucas Aguiar</title>
      <link>https://dev.to/lusqua</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lusqua"/>
    <language>en</language>
    <item>
      <title>MED 2.0 for devs: entities, fields and the implementation flow</title>
      <dc:creator>Lucas Aguiar</dc:creator>
      <pubDate>Mon, 24 Aug 2026 19:23:08 +0000</pubDate>
      <link>https://dev.to/woovi/med-20-for-devs-entities-fields-and-the-implementation-flow-51i5</link>
      <guid>https://dev.to/woovi/med-20-for-devs-entities-fields-and-the-implementation-flow-51i5</guid>
      <description>&lt;p&gt;If you work with Pix at a fintech, one thing is certain: sooner or later you'll have to deal with fraud. And the bar the Central Bank sets for that just went up. In August 2025 the &lt;strong&gt;MED 2.0&lt;/strong&gt; rules came out (starting with &lt;strong&gt;Resolution BCB nº 493&lt;/strong&gt;), the new version of Pix's Special Refund Mechanism — optional to adopt from &lt;strong&gt;Nov 23, 2025&lt;/strong&gt; and with a mandatory rollout scheduled for 2026.&lt;/p&gt;

&lt;p&gt;In this article I want to explain, in a simple and technical way, &lt;strong&gt;what the MED entities are&lt;/strong&gt;, &lt;strong&gt;the fields that matter in each one&lt;/strong&gt; and &lt;strong&gt;how the flow works end to end&lt;/strong&gt; — with a focus on whoever is going to write the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm8clhdge06rme1kyyips.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm8clhdge06rme1kyyips.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧩 First: what MED is (and why it exists)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;MED (Mecanismo Especial de Devolução — Special Refund Mechanism)&lt;/strong&gt; is a regulatory obligation from the Central Bank for &lt;strong&gt;every&lt;/strong&gt; Pix participant — banks, payment institutions, fintechs, anyone who does Pix.&lt;/p&gt;

&lt;p&gt;The idea is simple: when &lt;strong&gt;fraud&lt;/strong&gt; or an &lt;strong&gt;operational failure&lt;/strong&gt; happens, there must be a &lt;strong&gt;standardized and auditable&lt;/strong&gt; path to return the victim's money. Without MED, each institution would solve it its own way (or not at all).&lt;/p&gt;

&lt;p&gt;Important: MED is &lt;strong&gt;not&lt;/strong&gt; for commercial disputes, buyer's remorse, or "I sent a Pix to the wrong person because I wasn't paying attention." It's for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scam / fraud&lt;/strong&gt; (phishing, social engineering, coercion)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Account takeover&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fraudulent access&lt;/strong&gt; to channels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Institution's operational error&lt;/strong&gt; (duplicate Pix, wrong amount from a system failure)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Rule of thumb: MED = proven fraud or an error &lt;strong&gt;by the institution&lt;/strong&gt;. It's not a "cancel my Pix" channel.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff91e53wk2s9knzbagaav.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff91e53wk2s9knzbagaav.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 What changes in MED 2.0
&lt;/h2&gt;

&lt;p&gt;MED 1.0 had an Achilles' heel: it only blocked and refunded at the &lt;strong&gt;first receiving account&lt;/strong&gt; of the fraudulent Pix. So the scammer pulled the classic move — received the money and &lt;strong&gt;scattered it&lt;/strong&gt; in a cascade across many accounts in seconds. By the time the block arrived, the money had already evaporated. According to the BCB, in 2025 only &lt;strong&gt;9.3%&lt;/strong&gt; of the disputed amount was recovered on average.&lt;/p&gt;

&lt;p&gt;MED 2.0 tackles exactly that: it now &lt;strong&gt;traces the money's path beyond the first account&lt;/strong&gt;. The big highlights:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Multi-layer tracing.&lt;/strong&gt; DICT follows the money through the transaction graph and picks the transactions most likely to be on the drain path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Targeted blocking.&lt;/strong&gt; The amount is blocked at each node of the path — only what matters, not the whole account (more on this below).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refund in its own name.&lt;/strong&gt; The receiver's institution &lt;strong&gt;debits the mule account and refunds the victim directly&lt;/strong&gt;, appearing as the payer on the statement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Digital self-service.&lt;/strong&gt; The harmed customer triggers the process &lt;strong&gt;from the app&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standardized deadlines and UX&lt;/strong&gt; (Timing Manual and Minimum UX Requirements).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqc3f276txqsxzqshc52z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqc3f276txqsxzqshc52z.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧱 The MED entities (and how they connect)
&lt;/h2&gt;

&lt;p&gt;Before diving in, the most important thing is to understand the &lt;strong&gt;end-to-end flow&lt;/strong&gt;. It's what gives meaning to each entity:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;strong&gt;harmed customer&lt;/strong&gt; contacts their PSP, which opens a &lt;strong&gt;Funds Recovery&lt;/strong&gt; (&lt;code&gt;FundsRecovery&lt;/code&gt;). This is the starting point.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DICT traces&lt;/strong&gt; the transactions from the root transaction and sends &lt;strong&gt;Infraction Reports&lt;/strong&gt; (&lt;code&gt;InfractionReport&lt;/code&gt;) to the PSPs of the accounts along the money's path.&lt;/li&gt;
&lt;li&gt;Each receiving PSP &lt;strong&gt;blocks the balance&lt;/strong&gt; (only the indicated amount) and &lt;strong&gt;analyzes&lt;/strong&gt; the Infraction Report.&lt;/li&gt;
&lt;li&gt;Once the infractions are analyzed, the paying PSP triggers the start of the &lt;strong&gt;Refund Requests&lt;/strong&gt; — and the money goes back to the victim, layer by layer.&lt;/li&gt;
&lt;li&gt;Along the way, &lt;strong&gt;fraud markers&lt;/strong&gt; are recorded in DICT, feeding prevention.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So there are &lt;strong&gt;four entities&lt;/strong&gt;. Understand these four and you understand MED:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Entity&lt;/th&gt;
&lt;th&gt;Base endpoint&lt;/th&gt;
&lt;th&gt;Role in the flow&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Funds Recovery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/funds-recoveries/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Opens the case. The harmed customer starts it; it traces and orchestrates everything (the 2.0 highlight).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Infraction Report&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/infraction-reports/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;What each PSP in the chain receives. Triggers the &lt;strong&gt;block&lt;/strong&gt; and the analysis.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Refund Request&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/refunds/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Executes the refund of the money to the victim, layer by layer.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fraud Marker&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(automatic on agreement)&lt;/td&gt;
&lt;td&gt;Flags receivers as fraudsters in DICT, feeding prevention.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let's go through them one by one, in the order they appear in the flow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi1rkmludgerc6umvy9ct.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi1rkmludgerc6umvy9ct.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🕸️ Funds Recovery (&lt;code&gt;FundsRecovery&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;This is &lt;strong&gt;the big novelty&lt;/strong&gt; of MED 2.0 — and where everything begins. When the customer is a fraud victim, the paying PSP &lt;strong&gt;opens a Funds Recovery&lt;/strong&gt; (&lt;code&gt;POST /funds-recoveries/&lt;/code&gt;). From there, DICT traces the money and orchestrates blocks and refunds.&lt;/p&gt;

&lt;h3&gt;
  
  
  The stages (this is where the "several statuses" live)
&lt;/h3&gt;

&lt;p&gt;Funds Recovery isn't a single step — it moves through &lt;strong&gt;stages with their own statuses&lt;/strong&gt;, and your backend needs to react to each transition. Today the flow is &lt;strong&gt;fully automated&lt;/strong&gt;: DICT does the tracing, prioritization and dispatch of the Infraction Reports with no manual step from the PSP.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CREATED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Funds Recovery created over the root transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TRACKED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Transaction tracking graph created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AWAITING_ANALYSIS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Infraction Reports generated; receiving PSPs analyzing (up to 7 days)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ANALYSED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Infraction Reports analyzed and closed; the paying PSP can trigger the refund&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;After &lt;code&gt;ANALYSED&lt;/code&gt; comes the &lt;strong&gt;refund&lt;/strong&gt; (requests run sequentially) and, at the end, DICT signals completion through the &lt;code&gt;FUNDS_RECOVERY_COMPLETED&lt;/code&gt; event.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tracking graph parameters
&lt;/h3&gt;

&lt;p&gt;Parameters that bound the graph tracing (applied by DICT's algorithm):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;What it controls&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MinTransactionAmount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Minimum amount of each transaction considered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MaxTransactions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum number of transactions in the graph&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HopWindow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Time window between money entering and leaving an account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MaxHops&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum graph depth (how many layers to go down)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Main endpoints
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method + route&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST /funds-recoveries/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a Funds Recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /funds-recoveries/{id}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Query a Funds Recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST /funds-recoveries/{id}/tracking-graph&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a tracking graph&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /funds-recoveries/{id}/tracking-graph&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Query the graph&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST /funds-recoveries/{id}/block&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Request the transaction blocks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /funds-recoveries/{id}/infraction-reports&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List the Infraction Reports of the recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST /funds-recoveries/{id}/refund&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Request a refund within the recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /funds-recoveries/{id}/refunds&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List the refunds of the recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST /funds-recoveries/{id}/cancel&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cancel the recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🔔 &lt;strong&gt;Don't sit there polling.&lt;/strong&gt; DICT has an events endpoint (&lt;code&gt;GET /event-notifications&lt;/code&gt;) with two key signals for the recovery:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;FUNDS_RECOVERY_ANALYSED&lt;/code&gt; → the Infraction Reports were analyzed; you evaluate the result and &lt;strong&gt;trigger (or not) the start of the refund&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FUNDS_RECOVERY_COMPLETED&lt;/code&gt; → the refund phase is over; anyone still holding blocked funds &lt;strong&gt;can release them&lt;/strong&gt; (if there's no other basis to keep the block).&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr9nffkw8n16k5c942419.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr9nffkw8n16k5c942419.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  📢 Infraction Report (&lt;code&gt;InfractionReport&lt;/code&gt;) — the block
&lt;/h2&gt;

&lt;p&gt;If Funds Recovery is the customer pulling the trigger, the &lt;strong&gt;Infraction Report&lt;/strong&gt; is what lands on the &lt;strong&gt;PSPs&lt;/strong&gt; of the accounts along the money's path. Each prioritized transaction generates an Infraction Report for the PSP of that node, which then &lt;strong&gt;blocks the balance&lt;/strong&gt; and analyzes the case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Main fields
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TransactionId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;EndToEndId of the suspicious transaction (e.g., &lt;code&gt;E999990101234...&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;InfractionType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Type: &lt;code&gt;FRAUD&lt;/code&gt;, &lt;code&gt;REFUND_REQUEST&lt;/code&gt;, &lt;code&gt;REFUND_CANCELLED&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FundsRecoveryId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;(MED 2.0)&lt;/strong&gt; links the report to the Funds Recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;InfractionAmount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;(MED 2.0)&lt;/strong&gt; the amount that must be blocked from that transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;InfractionData.DebitedParticipant&lt;/code&gt; / &lt;code&gt;CreditedParticipant&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;ISPBs of the payer and the receiver&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;InfractionData.ReportedBy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;DEBITED_PARTICIPANT&lt;/code&gt; or &lt;code&gt;CREDITED_PARTICIPANT&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;InfractionData.TransactionDate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Transaction date-time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;InfractionData.InfractingAccountData&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Branch + account involved at the node&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TransactionDepth&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;(latest)&lt;/strong&gt; position of the transaction in the money-movement chain — how far it is from the initial fraud (DICT Manual v8.5)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;OPEN&lt;/code&gt;, &lt;code&gt;ACKNOWLEDGED&lt;/code&gt;, &lt;code&gt;CLOSED&lt;/code&gt;, &lt;code&gt;CANCELLED&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AnalysisResult&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AGREED&lt;/code&gt; or &lt;code&gt;DISAGREED&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The block: only the &lt;code&gt;InfractionAmount&lt;/code&gt;, not the whole account
&lt;/h3&gt;

&lt;p&gt;This is &lt;strong&gt;the part that causes the most bugs&lt;/strong&gt; if you're not careful. What you block is &lt;strong&gt;not&lt;/strong&gt; the account's entire balance, &lt;strong&gt;nor&lt;/strong&gt; the full amount of the original transaction — it's exactly the &lt;strong&gt;&lt;code&gt;InfractionAmount&lt;/code&gt;&lt;/strong&gt;: the amount that node received and that needs to be held on that specific transaction.&lt;/p&gt;

&lt;p&gt;Why? Because the money spreads out. A mule account may have received R$ 800 from one branch and R$ 200 from another; each Infraction Report carries the right amount to block on each transaction. Blocking too much holds the customer's legitimate money; blocking too little lets funds slip away. &lt;strong&gt;Block the &lt;code&gt;InfractionAmount&lt;/code&gt;, no more, no less.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⏱️ The receiving PSP has &lt;strong&gt;up to 7 days&lt;/strong&gt; to analyze and close the report (&lt;code&gt;.../acknowledge&lt;/code&gt; on receipt, &lt;code&gt;.../close&lt;/code&gt; with &lt;code&gt;AGREED&lt;/code&gt;/&lt;code&gt;DISAGREED&lt;/code&gt;). Every agreement generates a &lt;strong&gt;fraud marker&lt;/strong&gt; for that receiver — even if they never end up receiving a refund request.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  💸 Refund Request (&lt;code&gt;Refund&lt;/code&gt;) — bringing the money back
&lt;/h2&gt;

&lt;p&gt;Once the Infraction Reports are analyzed (&lt;code&gt;FUNDS_RECOVERY_ANALYSED&lt;/code&gt; event), the paying PSP &lt;strong&gt;triggers the start of the refund&lt;/strong&gt;. From there DICT fires the &lt;strong&gt;Refund Requests sequentially&lt;/strong&gt;, without you having to orchestrate them one by one.&lt;/p&gt;

&lt;h3&gt;
  
  
  The key point: each refund goes to the harmed customer, only for the missing amount
&lt;/h3&gt;

&lt;p&gt;Here lies the biggest mindset shift of 2.0. &lt;strong&gt;Every refund request is destined to the harmed customer&lt;/strong&gt; (the user of the paying PSP who opened the recovery), and the goal is to &lt;strong&gt;return the money wherever it is&lt;/strong&gt; in the chain. Rules that show up on the test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;1st request&lt;/strong&gt; always goes to the receiver of the &lt;strong&gt;root transaction&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;subsequent ones&lt;/strong&gt; follow the &lt;strong&gt;priority&lt;/strong&gt; defined in the blocking stage.&lt;/li&gt;
&lt;li&gt;Each PSP &lt;strong&gt;debits the mule account&lt;/strong&gt; and refunds &lt;strong&gt;directly to the harmed customer&lt;/strong&gt;, appearing as the &lt;strong&gt;payer&lt;/strong&gt; on the statement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Only the missing amount is requested&lt;/strong&gt; to complete the total — never the full initial amount again. BCB's own example: even with &lt;strong&gt;R$ 800 blocked&lt;/strong&gt; at a node, if R$ 250 was already recovered, the refund is requested &lt;strong&gt;for the remaining R$ 750 only&lt;/strong&gt;. The amounts are &lt;strong&gt;updated automatically&lt;/strong&gt; with each new refund.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words: the system keeps "scraping" whatever it can from each account until the total is met, never refunding more than owed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Main fields
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TransactionId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Transaction being refunded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RefundReason&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;FRAUD&lt;/code&gt;, &lt;code&gt;OPERATIONAL_FLAW&lt;/code&gt;, &lt;code&gt;REFUND_CANCELLED&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RefundAmount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Requested amount — &lt;strong&gt;the remainder&lt;/strong&gt;, not the full value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FundsRecoveryId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;(MED 2.0)&lt;/strong&gt; links the refund to the recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RefundAccount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;(MED 2.0)&lt;/strong&gt; destination account for the payment (the harmed customer's)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MonitorAccount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;(MED 2.0)&lt;/strong&gt; whether the account should be &lt;strong&gt;monitored&lt;/strong&gt; for future blocks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EffectiveRefundedAmount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;(MED 2.0)&lt;/strong&gt; on closure, the amount &lt;strong&gt;actually&lt;/strong&gt; refunded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;OPEN&lt;/code&gt;, &lt;code&gt;CLOSED&lt;/code&gt;, &lt;code&gt;CANCELLED&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AnalysisResult&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;TOTALLY_ACCEPTED&lt;/code&gt;, &lt;code&gt;PARTIALLY_ACCEPTED&lt;/code&gt;, &lt;code&gt;REJECTED&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RefundRejectionReason&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;NO_BALANCE&lt;/code&gt;, &lt;code&gt;ACCOUNT_CLOSURE&lt;/code&gt;, &lt;code&gt;CANNOT_REFUND&lt;/code&gt;, &lt;code&gt;OTHER&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Partial refund + monitoring is the nastiest real-world case.&lt;/strong&gt; If the account doesn't have the full balance at the moment (&lt;code&gt;PARTIALLY_ACCEPTED&lt;/code&gt; / &lt;code&gt;NO_BALANCE&lt;/code&gt;), you accept what you can, report the &lt;code&gt;EffectiveRefundedAmount&lt;/code&gt; and, with &lt;code&gt;MonitorAccount&lt;/code&gt; on, &lt;strong&gt;monitor the account&lt;/strong&gt; to keep blocking and refunding in parts as money comes in. Model this from the start — it's not an exception, it's routine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🚩 Fraud Marker (prevention)
&lt;/h2&gt;

&lt;p&gt;MED isn't only about putting out fires — it &lt;strong&gt;feeds prevention&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When an Infraction Report is &lt;strong&gt;agreed&lt;/strong&gt; (&lt;code&gt;AGREED&lt;/code&gt;), the receiver is &lt;strong&gt;flagged as a fraudster in DICT&lt;/strong&gt;, automatically. And there's an important 2.0 detail: &lt;strong&gt;all&lt;/strong&gt; receivers of transactions with an agreed report get flagged — &lt;strong&gt;even the ones that never receive&lt;/strong&gt; a refund request (e.g., a node that fell outside the continuous path of the graph).&lt;/p&gt;

&lt;p&gt;These markers feed DICT's anti-fraud data (counters of settlements, reported frauds and confirmed frauds, by key, by owner and by account).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🎯 &lt;strong&gt;Where this becomes gold:&lt;/strong&gt; query that anti-fraud data for the destination key/account &lt;strong&gt;before releasing a cash-out&lt;/strong&gt;. A recent fraud signal? You hold, ask for a double-check or add friction. Preventing is far cheaper than running the whole recovery flow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhi9inqbu2xk3rtl3lx14.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhi9inqbu2xk3rtl3lx14.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  📅 The deadlines you CAN'T miss
&lt;/h2&gt;

&lt;p&gt;A blown deadline in MED = money lost or a regulatory fine. Nail these into your scheduler (the definitive source is the &lt;strong&gt;Pix Timing Manual&lt;/strong&gt;):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Deadline&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Maximum transaction age to open a case&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;80 days&lt;/strong&gt; after the Pix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infraction Report analysis by the receiving PSP&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;up to 7 days&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Start the refund after the reports are analyzed&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;up to 72 hours&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Strong recommendation: model each deadline as a &lt;strong&gt;scheduled job&lt;/strong&gt; with alerts. Don't rely on someone watching a spreadsheet. A due SLA is a system event.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ✅ When to use (and NOT use) MED
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;You can use it for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proven scam/fraud&lt;/li&gt;
&lt;li&gt;Account takeover / coercion / fraudulent access&lt;/li&gt;
&lt;li&gt;The institution's own operational error&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;You can't use it for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commercial disputes ("the product never arrived")&lt;/li&gt;
&lt;li&gt;Buyer's remorse&lt;/li&gt;
&lt;li&gt;A Pix sent to the wrong person by the &lt;strong&gt;payer's own mistake&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Disputes between good-faith third parties&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🏁 Wrapping up
&lt;/h2&gt;

&lt;p&gt;MED 2.0 changes the game of fraud recovery on Pix: gone is the focus on a single account, in comes &lt;strong&gt;tracing the money's path&lt;/strong&gt;, with targeted blocking and layer-by-layer refunds. For your fintech, the summary is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧭 &lt;strong&gt;The flow starts with the customer&lt;/strong&gt;: they open the Funds Recovery, which traces and fires the Infraction Reports.&lt;/li&gt;
&lt;li&gt;🔒 &lt;strong&gt;Block the &lt;code&gt;InfractionAmount&lt;/code&gt;&lt;/strong&gt;, not the whole account — that's the detail that bites the most.&lt;/li&gt;
&lt;li&gt;💸 &lt;strong&gt;Each refund goes to the harmed customer and only for the missing amount&lt;/strong&gt; — the goal is to bring the money back wherever it is.&lt;/li&gt;
&lt;li&gt;🔔 &lt;strong&gt;Use events, not polling&lt;/strong&gt; (&lt;code&gt;FUNDS_RECOVERY_ANALYSED&lt;/code&gt;, &lt;code&gt;FUNDS_RECOVERY_COMPLETED&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;📚 The &lt;strong&gt;source of truth&lt;/strong&gt; is always the DICT API spec and the BCB rules — check the current version before coding.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Official sources (BCB)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.bcb.gov.br/estabilidadefinanceira/pix-normas" rel="noopener noreferrer"&gt;Pix rules — Central Bank of Brazil&lt;/a&gt; and &lt;strong&gt;Resolution BCB nº 493/2025&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.bcb.gov.br/content/estabilidadefinanceira/pix/MED/MED_2-0_Circuito_Pix-Dia_1.pdf" rel="noopener noreferrer"&gt;Circuito Pix — MED 2.0, Day 1&lt;/a&gt; and &lt;a href="https://www.bcb.gov.br/content/estabilidadefinanceira/pix/MED/MED_2-0_Circuito_Pix-Dia_2.pdf" rel="noopener noreferrer"&gt;Day 2&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.bcb.gov.br/content/estabilidadefinanceira/pix/Guia_MED.pdf" rel="noopener noreferrer"&gt;Refund procedures implementation guide (MED)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bacen/pix-dict-api" rel="noopener noreferrer"&gt;DICT API — official repository &lt;code&gt;bacen/pix-dict-api&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it! If you're implementing MED 2.0 at your fintech and hit a wall, drop a comment and let's talk it through. 🚀&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Understanding MongoDB $lookup performance</title>
      <dc:creator>Lucas Aguiar</dc:creator>
      <pubDate>Fri, 05 Sep 2025 00:30:01 +0000</pubDate>
      <link>https://dev.to/lusqua/understanding-mongodb-lookup-performance-1l15</link>
      <guid>https://dev.to/lusqua/understanding-mongodb-lookup-performance-1l15</guid>
      <description>&lt;p&gt;After running into some issues with $lookup in our banking application, I decided to dive deeper into how it actually works and why it sometimes ends up being so slow.&lt;/p&gt;

&lt;h4&gt;
  
  
  The problem
&lt;/h4&gt;

&lt;p&gt;It was just a normal day. I was working on a new feature when my tech lead mentioned me on Slack with something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Lucas, your query is too slow”&lt;br&gt;
(and then pasted the Explain Plan)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Right after that, he casually added: “&lt;em&gt;This query took 33 minutes to finish.&lt;/em&gt;”&lt;/p&gt;

&lt;p&gt;In my head, I was like: “&lt;em&gt;Wow… how did this slip through? The code has been there for three months already!&lt;/em&gt;”&lt;br&gt;
That’s when I decided to investigate the problem more deeply.&lt;/p&gt;

&lt;p&gt;Here’s the query he sent me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"simple start filtering"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$lookup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Company"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"let"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"company"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$company"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"$match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"removedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"$expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"$and"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"$eq"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"$_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"$$company"&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"as"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"company"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$lookup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Customer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"let"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"customer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$customer"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"$match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"removedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"$expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"$and"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"$eq"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"$_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"$$customer"&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"as"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customer"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"more filtering"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Right after reading the query, my first thought was: “Why are we using $expr and pipelines inside the $lookup?”&lt;/p&gt;

&lt;p&gt;So the very first thing I did was paste this aggregation pipeline into MongoDB Compass to run an explain and try to visualize why the query was so slow.&lt;br&gt;
And to my surprise… I just got an infinite loading screen 😅&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87zk8r3l6frtb74i3u26.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87zk8r3l6frtb74i3u26.png" alt=" " width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I promise, I waited a long time hoping to see some output. But in the end, all I got was a timeout error.&lt;/p&gt;

&lt;p&gt;At that point, I decided to break things down step by step, isolating each stage to figure out where the bottleneck was hiding.&lt;/p&gt;
&lt;h4&gt;
  
  
  Testing
&lt;/h4&gt;

&lt;p&gt;First, I ran an explain on just the $match. Everything worked perfectly: no problems at all. The query took only 10ms and used a simple company index.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"removedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"company"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ObjectId(&lt;/span&gt;&lt;span class="s2"&gt;"some_ID"&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TYPE_FILTERING"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frbbi71ws42nd8sc2izu9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frbbi71ws42nd8sc2izu9.png" alt=" " width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So far, so good.&lt;/p&gt;

&lt;p&gt;Next, I added the first $lookup and here’s where things started to smell funny. The execution time jumped from 10ms to 314ms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20ziigbdydzvvbeyx0ke.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20ziigbdydzvvbeyx0ke.png" alt=" " width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before moving on, I decided to test both $lookups together, just to confirm where the problem was. And… boom! 6 minutes to return the result. So clearly, the issue had to be in the $lookups.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vlncp1zlhhjtkc2y6m1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vlncp1zlhhjtkc2y6m1.png" alt=" " width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Understanding the lookup
&lt;/h4&gt;

&lt;p&gt;Everything here are based on the &lt;a href="https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/" rel="noopener noreferrer"&gt;official documentation of lookup on mongodb&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, to start, $lookup has this syntax and these fields&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
   $lookup:
     {
       from: &amp;lt;collection to join&amp;gt;,
       localField: &amp;lt;field from the input documents&amp;gt;,
       foreignField: &amp;lt;field from the documents of the "from" collection&amp;gt;,
       let: { &amp;lt;var_1&amp;gt;: &amp;lt;expression&amp;gt;, …, &amp;lt;var_n&amp;gt;: &amp;lt;expression&amp;gt; },
       pipeline: [ &amp;lt;pipeline to run&amp;gt; ],
       as: &amp;lt;output array field&amp;gt;
     }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the basic, the easy way to use an lookup is with &lt;strong&gt;localField&lt;/strong&gt; and &lt;strong&gt;foreignField&lt;/strong&gt;, but on our query we are using &lt;strong&gt;let&lt;/strong&gt; and &lt;strong&gt;pipeline&lt;/strong&gt; to find the results. &lt;br&gt;
But why? At the application we use soft delete to prevent deleting important data by an error, so everything keeps stored but with &lt;em&gt;removedAt&lt;/em&gt; flag, but we really needs to filter with pipeline?&lt;/p&gt;
&lt;h4&gt;
  
  
  Understanding the problem of &lt;code&gt;$expr&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;According to &lt;a href="https://www.mongodb.com/docs/manual/reference/operator/query/expr/" rel="noopener noreferrer"&gt;the official $expr documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When &lt;code&gt;$expr&lt;/code&gt; appears in a &lt;code&gt;$match&lt;/code&gt; stage that is part of a &lt;code&gt;$lookup&lt;/code&gt; subpipeline, &lt;code&gt;$expr&lt;/code&gt; can refer to let variables defined by the &lt;code&gt;$lookup&lt;/code&gt; stage&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problem here is that when MongoDB applies the filter, it has to evaluate the condition document by document inside the collection. As the collection grows, this operation gets slower and slower, since it can’t rely on indexes the same way a direct match does.&lt;/p&gt;

&lt;p&gt;In other words: &lt;code&gt;$expr&lt;/code&gt; inside a $lookup pipeline forces MongoDB to do more work, which quickly becomes painful at scale.&lt;/p&gt;

&lt;p&gt;The better approach is to use localField and foreignField whenever possible. That way, MongoDB can take advantage of indexes efficiently. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  {
    "$lookup": {
      "from": "Company",
      "localField": "company",
      "foreignField": "_id",
      "pipeline": [
        {
          "$match": {
            "removedAt": null,
          }
        }
      ],
      "as": "company"
    }
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here we go! 🎉&lt;br&gt;
Now the response time is totally acceptable:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk7cfesw5pdd7noi8kewv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk7cfesw5pdd7noi8kewv.png" alt=" " width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Improving the pipeline even more
&lt;/h4&gt;

&lt;p&gt;After some experiments, I discovered that whenever we use a pipeline inside a &lt;code&gt;$lookup&lt;/code&gt;, the query slows down a lot sometimes 5x to 10x slower.&lt;/p&gt;

&lt;p&gt;So, if your application doesn’t really need extra validations, I strongly recommend avoiding the &lt;code&gt;pipeline&lt;/code&gt;. Instead, stick to &lt;code&gt;localField&lt;/code&gt;/&lt;code&gt;foreignField&lt;/code&gt;. This is much simpler and far more efficient when you’re just joining on _ids.&lt;/p&gt;

&lt;p&gt;In my case, by removing the unnecessary &lt;code&gt;pipeline&lt;/code&gt;, I improved the query from 259ms down to 36ms. Here’s the simplified version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  {
    "$match": "simple filtering"
  },
  {
    "$lookup": {
      "from": "Company",
      "localField": "company",
      "foreignField": "_id",
      "as": "company"
    }
  },
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;After all the tweaks, the final query ended up looking like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  {
    "$match": "simple filtering"
  },
  {
    "$lookup": {
      "from": "Company",
      "localField": "company",
      "foreignField": "_id",
      "as": "company",
    }
  },
  {
    "$lookup": {
      "from": "Customer",
      "localField": "customer",
      "foreignField": "_id",
      "as": "customer",
    }
  },
  {
    "$match": "more filtering"
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpui0kzc1g2xlvwo8m92x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpui0kzc1g2xlvwo8m92x.png" alt=" " width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s it a (not so short) story, but with some practical tips on how to improve $lookup performance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Prefer &lt;code&gt;localField&lt;/code&gt; / &lt;code&gt;foreignField&lt;/code&gt; whenever possible&lt;/li&gt;
&lt;li&gt;⚠️ $expr can slow down queries a lot&lt;/li&gt;
&lt;li&gt;🛠️ For more complex lookups, combine &lt;code&gt;pipeline&lt;/code&gt; with &lt;code&gt;localField&lt;/code&gt;/&lt;code&gt;foreignField&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Understanding IMAP and SMTP Mail Protocols</title>
      <dc:creator>Lucas Aguiar</dc:creator>
      <pubDate>Sat, 26 Jul 2025 01:48:38 +0000</pubDate>
      <link>https://dev.to/woovi/understanding-imap-and-smtp-mail-protocols-1bch</link>
      <guid>https://dev.to/woovi/understanding-imap-and-smtp-mail-protocols-1bch</guid>
      <description>&lt;p&gt;During a recent implementation of a customer service platform, I encountered several configuration challenges related to email communication protocols. This experience prompted me to conduct thorough research into SMTP and IMAP protocols, their interactions, and best implementation practices. The knowledge gained proved invaluable for resolving our technical issues, and I'm sharing these insights to benefit others facing similar challenges in their systems architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  📮 Introduction
&lt;/h2&gt;

&lt;p&gt;In the digital world, sending and receiving emails are fundamental communication processes. During my recent experience, I realized that understanding how these protocols work behind the scenes can make all the difference, especially when configuring or troubleshooting customer service systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  🌐 Overview of Email Protocols
&lt;/h2&gt;

&lt;p&gt;Emails travel between computers using standardized protocols. The most common ones are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SMTP (Simple Mail Transfer Protocol)&lt;/strong&gt;: Used for sending messages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IMAP (Internet Message Access Protocol)&lt;/strong&gt;: Used for accessing and managing messages stored on the server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While POP3 also exists, this article focuses on SMTP and IMAP, which offer advantages for synchronization and email management across multiple devices.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvs5286a2ee98i0571sq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvs5286a2ee98i0571sq.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  📤 SMTP - Simple Mail Transfer Protocol
&lt;/h2&gt;

&lt;p&gt;SMTP is the "engine" that sends emails. Here's a summary of what I learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Primary Function&lt;/strong&gt;: SMTP is responsible for sending messages from the sender to the server, and between servers, ensuring the email reaches its recipient.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How It Works&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;The process begins with the email client sending the message to the SMTP server&lt;/li&gt;
&lt;li&gt;Commands like EHLO/HELO, MAIL FROM, RCPT TO, and DATA structure this communication&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Ports and Security&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Common ports include 25 (traditional) and 587 (for authenticated sending)&lt;/li&gt;
&lt;li&gt;Using TLS or SSL encryption is highly recommended to protect data during transmission&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9xv1ytfz4rcg5k9ek7l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9xv1ytfz4rcg5k9ek7l.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  📥 IMAP - Internet Message Access Protocol
&lt;/h2&gt;

&lt;p&gt;While SMTP handles sending, IMAP is the "manager" of received emails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Primary Function&lt;/strong&gt;: IMAP allows you to access and manage emails directly on the server. This means that regardless of which device you use—smartphone, tablet, or computer—your messages remain synchronized.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advantages over POP3&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Messages remain stored on the server, allowing viewing and organization from any device&lt;/li&gt;
&lt;li&gt;You can create, move, and delete folders directly on the server without downloading emails&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;How It Works&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;IMAP synchronizes message status (e.g., read/unread) and organizes emails into folders&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Ports and Security&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Port 143 is used for unencrypted connections, while port 993 is for secure connections via SSL/TLS&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F83celu7utrwjfeyjxpm7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F83celu7utrwjfeyjxpm7.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚖️ Comparison Between SMTP and IMAP
&lt;/h2&gt;

&lt;p&gt;Although both protocols are part of email communication, they have distinct, complementary roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SMTP&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Responsible for sending emails&lt;/li&gt;
&lt;li&gt;Functions as the "digital mail carrier" distributing messages between servers&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;IMAP&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Responsible for accessing and managing stored messages&lt;/li&gt;
&lt;li&gt;Allows you to track your emails in a synchronized manner across multiple devices&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx5b2exe0nuam5cu3pnut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx5b2exe0nuam5cu3pnut.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Technical Aspects and Implementation
&lt;/h2&gt;

&lt;p&gt;During my experience, I found that visualizing the email flow helps better understand how these protocols interact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sending and Receiving Flow&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;The email leaves the client and goes to the SMTP server&lt;/li&gt;
&lt;li&gt;From there, it may be forwarded to other servers until it reaches the recipient's server&lt;/li&gt;
&lt;li&gt;Finally, the email client uses IMAP to access and manage this message on the server&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Configuration and Authentication&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Correctly configuring ports and authentication methods (such as TLS/SSL) is essential to ensure everything works smoothly&lt;/li&gt;
&lt;li&gt;In many situations, the challenge lies precisely in aligning these configurations for efficient and secure customer service operation&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe94jtlo60lldh614oqaa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe94jtlo60lldh614oqaa.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🔒 Security and Best Practices
&lt;/h2&gt;

&lt;p&gt;To avoid headaches (like the one I faced), it's important to pay attention to these points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Common Threats&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Without proper security, data can be intercepted during transmission&lt;/li&gt;
&lt;li&gt;Configuration errors can create vulnerabilities for spam or even more serious attacks&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Security Measures&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Always use encryption (SSL/TLS) in communications&lt;/li&gt;
&lt;li&gt;Regularly verify and update server authentication configurations&lt;/li&gt;
&lt;li&gt;Follow recommended best practices for both administrators and end users&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  ✅ Conclusion
&lt;/h2&gt;

&lt;p&gt;Understanding SMTP and IMAP protocols was essential to solving the problem I encountered when configuring the customer service system. With SMTP handling sending and IMAP enabling access and message management, both complement each other to make email communication fluid and secure.&lt;/p&gt;

&lt;p&gt;I hope this article, written simply and based on my experience, helps you better understand how these protocols work. After all, sharing knowledge is a way to grow and make life easier for those who also need to deal with these configurations daily.&lt;/p&gt;




&lt;p&gt;If you have questions or want to explore any topic in greater depth, feel free to comment. We're all learning together!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Prometheus + Redis: Simple Guide to Monitor Redis Instances</title>
      <dc:creator>Lucas Aguiar</dc:creator>
      <pubDate>Mon, 21 Apr 2025 22:26:33 +0000</pubDate>
      <link>https://dev.to/woovi/prometheus-redis-simple-guide-to-monitor-redis-instances-n1f</link>
      <guid>https://dev.to/woovi/prometheus-redis-simple-guide-to-monitor-redis-instances-n1f</guid>
      <description>&lt;p&gt;Redis is often a critical part of modern infrastructure — whether used as a cache, message broker, or ephemeral store. Monitoring it properly helps you detect issues early and ensure system stability.&lt;/p&gt;

&lt;p&gt;In this guide, you’ll learn how to build a Redis monitoring dashboard with Prometheus and Grafana to quickly identify issues and improve system reliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Installing Prometheus (Quick Setup)
&lt;/h2&gt;

&lt;p&gt;We’ll start by deploying Prometheus using Helm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

helm install kube-prometheus prometheus-community/kube-prometheus-stack \
  --namespace prometheus --create-namespace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This will install Prometheus, Grafana, and some built-in exporters.&lt;br&gt;
To access Grafana:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl port-forward svc/kube-prometheus-grafana -n prometheus 3000:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can access here -&amp;gt; &lt;a href="http://localhost:3000/login" rel="noopener noreferrer"&gt;http://localhost:3000/login&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Default login: admin / prom-operator&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🧱 Installing Redis
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

helm install redis bitnami/redis \
  --namespace app-stg \
  --set architecture=standalone \
  --set auth.enabled=false \
  --create-namespace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This installs a standalone Redis with no password, perfect for testing.&lt;br&gt;
After this, you can point the Redis Exporter and your application to the correct Redis service. The hostname will be:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;redis-master.app-stg.svc.cluster.local:6379
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📦 Installing Redis Exporter
&lt;/h2&gt;

&lt;p&gt;Now let’s deploy the Redis Exporter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm install redis-exporter prometheus-community/prometheus-redis-exporter \
  --namespace prometheus \
  --set serviceMonitor.enabled=true \
  --set serviceMonitor.namespace=prometheus \
  --set serviceMonitor.interval=15s \
  --set serviceMonitor.labels.release=kube-prometheus \
  --set redisAddress=redis://redis-master.app-stg.svc.cluster.local:6379 \
  --create-namespace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Redis Exporter exposes metrics at a Prometheus-compatible /metrics endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl port-forward svc/redis-exporter-prometheus-redis-exporter -n prometheus 9121:9121
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open: &lt;a href="http://localhost:9121/metrics" rel="noopener noreferrer"&gt;http://localhost:9121/metrics&lt;/a&gt;&lt;br&gt;
You should see a long list of metrics like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6uk08hc0buxzlke9ans.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6uk08hc0buxzlke9ans.png" alt="Metrics example print screen" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ redis_up 1 means the exporter is connected to a Redis instance and it’s healthy.&lt;br&gt;
🚫 redis_up 0 means the exporter failed to connect — check the Redis address or service availability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If everything is working, you’ll also see metrics like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;redis_connected_clients&lt;/li&gt;
&lt;li&gt;redis_memory_used_bytes&lt;/li&gt;
&lt;li&gt;redis_total_commands_processed&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  🔍 Checking in Prometheus
&lt;/h2&gt;

&lt;p&gt;To validate that Prometheus is collecting Redis metrics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl port-forward svc/kube-prometheus-kube-prome-prometheus -n prometheus 9090
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;a href="http://localhost:9090" rel="noopener noreferrer"&gt;http://localhost:9090&lt;/a&gt;, go to &lt;a href="http://localhost:9090/targets" rel="noopener noreferrer"&gt;Status &amp;gt; Targets&lt;/a&gt;, and check that Redis Exporter is listed and UP.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi7s4xgevvwa2crwfv43b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi7s4xgevvwa2crwfv43b.png" alt="Prometheus target debuging" width="799" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything is working right!&lt;/p&gt;

&lt;h2&gt;
  
  
  📊 Importing the Redis Dashboard on Grafana
&lt;/h2&gt;

&lt;p&gt;To visualize your Redis metrics in a clean and ready-to-use panel, you can import a community-maintained dashboard from Grafana Labs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://grafana.com/grafana/dashboards/14091-redis-dashboard-for-prometheus-redis-exporter-1-x/" rel="noopener noreferrer"&gt;https://grafana.com/grafana/dashboards/14091-redis-dashboard-for-prometheus-redis-exporter-1-x/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🧭 How to Import
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Access Grafana by establishing port forwarding and navigating to &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; in your browser&lt;/li&gt;
&lt;li&gt;Navigate to the "+" icon in the left sidebar and select "Import"&lt;/li&gt;
&lt;li&gt;Enter the dashboard ID or URL in the provided field and click "Load"&lt;/li&gt;
&lt;li&gt;Click "Import" to finalize the installation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Allow a few moments for the system to process your request as the metrics populate on your dashboard&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl46lkebqnsnph5s743dx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl46lkebqnsnph5s743dx.png" alt="Grafana redis dashboard" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thats it!&lt;br&gt;
⎈Happy Helming!⎈&lt;/p&gt;

</description>
      <category>redis</category>
      <category>prometheus</category>
      <category>grafana</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Explorando um Cluster MongoDB com Terraform</title>
      <dc:creator>Lucas Aguiar</dc:creator>
      <pubDate>Sun, 02 Feb 2025 02:12:41 +0000</pubDate>
      <link>https://dev.to/lusqua/explorando-um-cluster-mongodb-com-terraform-19fm</link>
      <guid>https://dev.to/lusqua/explorando-um-cluster-mongodb-com-terraform-19fm</guid>
      <description>&lt;p&gt;A pouco tempo após algumas conversas me interessei por aprender mais sobre terraform e como provisionar infraestrutura de maneira mais fácil, simples de replicar em outros lugares e manter a longo prazo.&lt;/p&gt;

&lt;p&gt;A ideia aqui é demonstrar como a abordagem de Infrastructure as Code pode simplificar a criação de um cluster MongoDB, composto por um nó primário e dois nós secundários. Utilizando o Terraform e o Docker como provedor para teste local.&lt;/p&gt;

&lt;h2&gt;
  
  
  O passo a passo
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Preparando o ambiente
&lt;/h3&gt;

&lt;p&gt;Primeiro, vamos definir o docker como provedor e uma rede para que os containers se comuniquem usando a resolução de DNS interno, podendo usar "mongo_primary", "mongo_secondary_1" e "mongo_secondary_2" apontando para os ips internos corretos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;docker&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"kreuzwerker/docker"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 3.0.2"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"docker"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"docker_network"&lt;/span&gt; &lt;span class="s2"&gt;"mongo_network"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mongo_network"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Gerando os arquivos internos de autenticação
&lt;/h3&gt;

&lt;p&gt;Antes de subir os containers, é necessário gerar um arquivo keyfile que será utilizado para a autenticação interna entre os nós do replica set do MongoDB. Esse arquivo garante que somente os nós autorizados possam se comunicar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl rand &lt;span class="nt"&gt;-base64&lt;/span&gt; 756 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; mongo-keyfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alterando as permissões para que somente o proprietário possa ler o arquivo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;400 mongo-keyfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Certifique-se de que o arquivo mongo-keyfile esteja na mesma pasta onde se encontra o arquivo main.tf ( ou em uma pasta no mesmo nível ), pois ele será referenciado nos volumes dos containers.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Configurando o replicaset
&lt;/h3&gt;

&lt;p&gt;Separamos aqui os containers em duas partes 1 nó primário e 2 secundários, em um cluster replica set de mongodb o &lt;strong&gt;nó primário&lt;/strong&gt; é responsável por aceitar operações de escrita e propagar as alterações para os &lt;strong&gt;secundários&lt;/strong&gt;, que mantêm uma cópia dos dados e podem ser utilizados para balanceamento de carga em leituras.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// init-replica.js&lt;/span&gt;

&lt;span class="nx"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Mongo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mongodb://admin:adminpass@localhost:27017&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ℹ️ Replica Set já configurado.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;codeName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NotYetInitialized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🚀 Iniciando Replica Set...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initiate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rs0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;members&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mongo_primary:27017&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mongo_secondary_1:27017&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mongo_secondary_2:27017&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;✅ Replica Set inicializado com sucesso!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;❌ Erro ao verificar status do Replica Set:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Subindo os containers mongodb
&lt;/h3&gt;

&lt;p&gt;No terraform a configuração dos containers mongodb é similar, entretanto, só o nó primário recebe instruções de exposição das portas e o script de configuração do cluster.&lt;/p&gt;

&lt;p&gt;Montamos os dois volumes referente ao keyfile de autenticação e o script de configuração de replica em js.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"docker_container"&lt;/span&gt; &lt;span class="s2"&gt;"mongo_primary"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mongo_primary"&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mongo:7.0"&lt;/span&gt;
  &lt;span class="nx"&gt;restart&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"always"&lt;/span&gt;

  &lt;span class="nx"&gt;networks_advanced&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;docker_network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mongo_network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;ports&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;internal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;27017&lt;/span&gt;
    &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;27017&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;volumes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;host_path&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;abspath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/mongo-keyfile"&lt;/span&gt;
    &lt;span class="nx"&gt;container_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/data/keyfile"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;volumes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;host_path&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;abspath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/init-replica.js"&lt;/span&gt;
    &lt;span class="nx"&gt;container_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/docker-entrypoint-initdb.d/init-replica.js"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"MONGO_INITDB_ROOT_USERNAME=admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"MONGO_INITDB_ROOT_PASSWORD=adminpass"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"MONGO_REPLICA_SET_NAME=rs0"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"mongod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"--replSet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"rs0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"--keyFile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/data/keyfile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"--bind_ip_all"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="k"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"local-exec"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sleep 10 &amp;amp;&amp;amp; docker exec mongo_primary mongosh /docker-entrypoint-initdb.d/init-replica.js"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"docker_container"&lt;/span&gt; &lt;span class="s2"&gt;"mongo_secondary_1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mongo_secondary_1"&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mongo:7.0"&lt;/span&gt;
  &lt;span class="nx"&gt;restart&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"always"&lt;/span&gt;

  &lt;span class="nx"&gt;networks_advanced&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;docker_network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mongo_network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;volumes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;host_path&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;abspath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/mongo-keyfile"&lt;/span&gt;
    &lt;span class="nx"&gt;container_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/data/keyfile"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"MONGO_INITDB_ROOT_USERNAME=admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"MONGO_INITDB_ROOT_PASSWORD=adminpass"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"MONGO_REPLICA_SET_NAME=rs0"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"mongod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"--replSet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"rs0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"--keyFile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/data/keyfile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"--bind_ip_all"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"docker_container"&lt;/span&gt; &lt;span class="s2"&gt;"mongo_secondary_2"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mongo_secondary_2"&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mongo:7.0"&lt;/span&gt;
  &lt;span class="nx"&gt;restart&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"always"&lt;/span&gt;

  &lt;span class="nx"&gt;networks_advanced&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;docker_network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mongo_network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;volumes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;host_path&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;abspath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/mongo-keyfile"&lt;/span&gt;
    &lt;span class="nx"&gt;container_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/data/keyfile"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"MONGO_INITDB_ROOT_USERNAME=admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"MONGO_INITDB_ROOT_PASSWORD=adminpass"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"MONGO_REPLICA_SET_NAME=rs0"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"mongod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"--replSet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"rs0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"--keyFile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/data/keyfile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"--bind_ip_all"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Integração com aplicações
&lt;/h3&gt;

&lt;p&gt;Uma vez com o cluster configurado, é possível se conectar a ele através do comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mongo_primary mongosh &lt;span class="s2"&gt;"mongodb://admin:adminpass@localhost:27017/?replicaSet=rs0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para que uma aplicação tenha acesso ao cluster, é necessário que ela esteja junto a "mongo_network" para que não haja problemas na resolução de DNS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Este exemplo demonstra como o terraform pode ser uma ferramenta poderosa para criar ambientes consistentes e fáceis de serem replicados, lembrando que o exemplo anterior não é recomendado para uso em produção pois cada nó deve estar em uma máquina separada para garantir alta disponibilidade.&lt;/p&gt;

&lt;p&gt;Caso tenha ficado algo confuso, você pode acompanhar meu código no Github:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lusqua/terraform-mongodb-cluster" rel="noopener noreferrer"&gt;https://github.com/lusqua/terraform-mongodb-cluster&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Desvendando a VPC na AWS: Subnets, Gateways e Configuração de Rede</title>
      <dc:creator>Lucas Aguiar</dc:creator>
      <pubDate>Thu, 31 Oct 2024 10:30:00 +0000</pubDate>
      <link>https://dev.to/lusqua/desvendando-a-vpc-na-aws-subnets-gateways-e-configuracao-de-rede-4lb6</link>
      <guid>https://dev.to/lusqua/desvendando-a-vpc-na-aws-subnets-gateways-e-configuracao-de-rede-4lb6</guid>
      <description>&lt;p&gt;Ontem à noite, após o expediente, decidi resolver alguns problemas técnicos em um side project na AWS. O desafio? Criar uma infraestrutura robusta, com subnets públicas e privadas, garantindo que apenas os recursos certos tivessem acesso à internet. A ideia era entender de uma vez por todas como cada peça se encaixa: Internet Gateway, NAT Gateway e os detalhes de roteamento.&lt;/p&gt;

&lt;p&gt;O que parecia simples se revelou mais complexo. Ao configurar meu Auto Scaling Group, notei que as instâncias EC2 não estavam acessando a internet, mesmo quando atribuídas a subnets públicas. Isso me fez mergulhar fundo nas configurações de roteamento, nas permissões de IP público e nas nuances da AWS.&lt;/p&gt;

&lt;p&gt;Aqui estão as lições e conceitos principais que consegui extrair desse sofrimento desenfreado:&lt;/p&gt;

&lt;h3&gt;
  
  
  Subnet Pública vs. Privada
&lt;/h3&gt;

&lt;p&gt;Para simplificar, uma subnet pública permite que instâncias tenham acesso direto à internet através de um Internet Gateway (IGW), essencial para servidores web ou APIs externas. No entanto, minha aplicação precisava de segurança adicional, então configurei uma subnet privada para meus recursos internos, como bancos de dados. Essas subnets privadas não têm acesso direto à internet, mas, com um NAT Gateway, consegui permitir que os recursos façam atualizações ou se comuniquem externamente quando necessário.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2nvj70oq9pudhxzq9bj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2nvj70oq9pudhxzq9bj.png" alt="Um diagrama retangular com uma subnet pública à esquerda, conectada diretamente à internet via Internet Gateway. À direita, a subnet privada se conecta indiretamente à internet através de um NAT Gateway, exibindo recursos internos protegidos, como bancos de dados." width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Configurando o Internet Gateway e o NAT Gateway
&lt;/h3&gt;

&lt;p&gt;Para as subnets públicas, configurei um Internet Gateway e adicionei uma rota que apontava 0.0.0.0/0 para ele, o que garantiu que as instâncias pudessem enviar e receber tráfego da internet. Já para as subnets privadas, configurei um NAT Gateway na subnet pública, permitindo que o tráfego externo fosse roteado indiretamente. Isso evitou a exposição direta à internet, mas ainda permitiu que as instâncias privadas acessem atualizações externas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd910xf58v6mvbp66pm9s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd910xf58v6mvbp66pm9s.png" alt="A imagem mostra uma subnet pública conectada a um Internet Gateway para tráfego direto de internet, enquanto uma subnet privada se conecta indiretamente via NAT Gateway, que reside na subnet pública." width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Desafios com o Auto Scaling e Distribuição de Tarefas
&lt;/h3&gt;

&lt;p&gt;Um dos problemas mais complexos foi garantir que as tarefas do ECS fossem distribuídas entre as instâncias EC2 de maneira balanceada. Inicialmente, minhas tarefas não se espalhavam como esperado, então experimentei diferentes estratégias de Task Placement, como “AZ balanced binpack”, que ajudou a otimizar o uso das instâncias e garantir redundância entre as zonas de disponibilidade.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0iujc8t25266arxo4fns.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0iujc8t25266arxo4fns.png" alt="Um diagrama de várias instâncias EC2 espalhadas por zonas de disponibilidade (AZs) com tarefas ECS distribuídas uniformemente entre elas, utilizando a estratégia “AZ balanced binpack” para otimização e redundância de recursos." width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Após uma noite inteira, finalmente consegui configurar uma arquitetura estável, com recursos bem distribuídos e segura. Este side project foi uma experiência reveladora, me dando não apenas o domínio sobre subnets e gateways na AWS, mas também uma lição sobre resiliência e atenção aos detalhes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusão
&lt;/h3&gt;

&lt;p&gt;O processo de configurar subnets públicas e privadas, Internet Gateway, e NAT Gateway pode ser complicado, mas é essencial para arquiteturas seguras e escaláveis. Para quem enfrenta os mesmos desafios, espero que este artigo forneça uma visão prática e inspiradora para perseverar, mesmo que isso signifique uma longa noite de trabalho.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>learning</category>
      <category>network</category>
    </item>
    <item>
      <title>Automatizando deployments com WatchTower e GitHub Container Registry</title>
      <dc:creator>Lucas Aguiar</dc:creator>
      <pubDate>Tue, 08 Oct 2024 17:05:55 +0000</pubDate>
      <link>https://dev.to/lusqua/automatizando-atualizacoes-de-containers-com-watchtower-e-github-container-registry-1l7c</link>
      <guid>https://dev.to/lusqua/automatizando-atualizacoes-de-containers-com-watchtower-e-github-container-registry-1l7c</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk2jjiblog4farzqybpa6.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk2jjiblog4farzqybpa6.jpeg" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Manter aplicações atualizadas é crucial para garantir segurança, performance e a incorporação de novas funcionalidades. No mundo dos containers Docker, WatchTower surge como uma ferramenta poderosa para automatizar esse processo. Neste artigo, vamos explorar como integrar o WatchTower com o GitHub Container Registry (GHCR), criando um fluxo contínuo de atualização para seus containers. Vamos abordar desde a configuração inicial até um exemplo prático para ilustrar todo o processo.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Introdução ao WatchTower e GHCR
&lt;/h2&gt;

&lt;p&gt;O que é o WatchTower?&lt;/p&gt;

&lt;p&gt;WatchTower é uma ferramenta de código aberto que monitora seus containers Docker e automaticamente os atualiza quando uma nova imagem é detectada no registry configurado. Isso elimina a necessidade de intervenções manuais para manter suas aplicações sempre atualizadas com as últimas versões.&lt;/p&gt;

&lt;p&gt;O que é o GitHub Container Registry (GHCR)?&lt;/p&gt;

&lt;p&gt;O GitHub Container Registry é um serviço de registro de containers integrado ao GitHub, permitindo que você armazene e gerencie suas imagens Docker diretamente nos repositórios do GitHub. Ele oferece integração perfeita com workflows de CI/CD, facilitando a publicação e o gerenciamento de imagens.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ Pré-requisitos
&lt;/h2&gt;

&lt;p&gt;Antes de começarmos, certifique-se de ter o seguinte:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker instalado na sua máquina ou servidor.&lt;/li&gt;
&lt;li&gt;Conta no GitHub com acesso ao GitHub Container Registry.&lt;/li&gt;
&lt;li&gt;Repositório GitHub para hospedar sua aplicação e workflow.&lt;/li&gt;
&lt;li&gt;Conhecimentos básicos de Docker e GitHub Actions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔧 Passo a Passo: Integrando WatchTower com GHCR&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Configurando o GitHub Container Registry&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Primeiro, vamos configurar o GHCR para armazenar nossas imagens Docker.&lt;/p&gt;

&lt;p&gt;a. Autenticação no GHCR&lt;/p&gt;

&lt;p&gt;Para autenticar o Docker com o GHCR, você precisa de um token de acesso pessoal com permissão para &lt;code&gt;write:packages&lt;/code&gt; e &lt;code&gt;read:packages&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;echo $CR_PAT | docker login ghcr.io -u USERNAME --password-stdin&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Substitua CR_PAT pelo seu token de acesso pessoal e USERNAME pelo seu nome de usuário do GitHub.&lt;/p&gt;

&lt;p&gt;b. Criando uma Imagem Docker&lt;/p&gt;

&lt;p&gt;Vamos criar uma imagem Docker simples para este exemplo. Crie um arquivo Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Dockerfile
FROM node:14-alpine
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E um arquivo package.json básico:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "watchtower-example",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "express": "^4.17.1"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Crie também um index.js simples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) =&amp;gt; {
  res.send('Hello from WatchTower and GHCR!');
});

app.listen(port, () =&amp;gt; {
  console.log(`App running on port ${port}`);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;c. Construindo e Publicando a Imagem&lt;/p&gt;

&lt;p&gt;Construa a imagem Docker e publique-a no GHCR.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker build -t ghcr.io/USERNAME/watchtower-example:latest .&lt;/code&gt;&lt;br&gt;
&lt;code&gt;docker push ghcr.io/USERNAME/watchtower-example:latest&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Substitua &lt;code&gt;USERNAME&lt;/code&gt; pelo seu &lt;strong&gt;nome de usuário&lt;/strong&gt; do GitHub.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Configurando o WatchTower&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Agora, vamos configurar o WatchTower para monitorar o GHCR e atualizar automaticamente nossos containers.&lt;/p&gt;

&lt;p&gt;a. Criando um Arquivo de Configuração do WatchTower&lt;/p&gt;

&lt;p&gt;Crie um arquivo &lt;strong&gt;docker-compose.yml&lt;/strong&gt; para orquestrar o WatchTower e sua aplicação.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3'

services:
  app:
    image: ghcr.io/USERNAME/watchtower-example:latest
    container_name: watchtower-example
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production

  watchtower:
    image: containrrr/watchtower
    container_name: watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WATCHTOWER_CLEANUP=true
      - WATCHTOWER_POLL_INTERVAL=300
    command: --registry ghcr.io --interval 300
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Explicações:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;app: Serviço da aplicação que será monitorada pelo WatchTower.&lt;/li&gt;
&lt;li&gt;watchtower: Serviço do WatchTower configurado para monitorar o GHCR com um intervalo de verificação de 5 minutos (300 segundos).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;b. Variáveis de Ambiente para Autenticação&lt;/p&gt;

&lt;p&gt;Para que o WatchTower possa acessar o GHCR privado, precisamos fornecer as credenciais. Atualize o serviço watchtower no docker-compose.yml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;environment:
  - WATCHTOWER_CLEANUP=true
  - WATCHTOWER_POLL_INTERVAL=300
  - WATCHTOWER_REGISTRY_AUTH=true
  - WATCHTOWER_REGISTRY_USERNAME=USERNAME
  - WATCHTOWER_REGISTRY_PASSWORD=CR_PAT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Substitua &lt;strong&gt;USERNAME&lt;/strong&gt; pelo &lt;strong&gt;seu nome&lt;/strong&gt; de usuário do GitHub e &lt;strong&gt;CR_PAT&lt;/strong&gt; pelo &lt;strong&gt;seu token&lt;/strong&gt; de acesso pessoal.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Automatizando a Atualização com GitHub Actions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Para garantir que sempre que você fizer push no repositório, a imagem Docker seja atualizada no GHCR, vamos configurar um workflow no GitHub Actions.&lt;/p&gt;

&lt;p&gt;Crie um arquivo .github/workflows/docker-publish.yml no seu repositório:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Publish Docker image

on:
  push:
    branches: [ main ]

jobs:
  build-and-push:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: ghcr.io/USERNAME/watchtower-example:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Explicações:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;actions/checkout@v3: Faz o checkout do código.&lt;/li&gt;
&lt;li&gt;docker/setup-buildx-action@v3: Configura o Docker Buildx.&lt;/li&gt;
&lt;li&gt;docker/login-action@v3: Autentica no GHCR usando o token do GitHub.&lt;/li&gt;
&lt;li&gt;docker/build-push-action@v4: Constrói e publica a imagem no GHCR.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Substitua &lt;strong&gt;USERNAME&lt;/strong&gt; pelo seu &lt;strong&gt;nome de usuário&lt;/strong&gt; do GitHub.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Implementando e Testando&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;a. Deploy Inicial&lt;/p&gt;

&lt;p&gt;Inicie os serviços usando o Docker Compose:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker-compose up -d&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Isso &lt;strong&gt;iniciará sua aplicação&lt;/strong&gt; e o WatchTower. O WatchTower começará a &lt;strong&gt;monitorar o GHCR a cada 5 minutos&lt;/strong&gt; (300 segundos) para verificar se há novas imagens disponíveis.&lt;/p&gt;

&lt;p&gt;b. Atualizando a Aplicação&lt;/p&gt;

&lt;p&gt;Faça uma alteração no código da sua aplicação, por exemplo, atualize a mensagem em index.js:&lt;/p&gt;

&lt;p&gt;res.send('Hello from WatchTower, GHCR, and your updated app!');&lt;/p&gt;

&lt;p&gt;Commit e push as alterações para o repositório GitHub:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git add .&lt;/code&gt;&lt;br&gt;
&lt;code&gt;git commit -m "Update welcome message"&lt;/code&gt;&lt;br&gt;
&lt;code&gt;git push origin main&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;O GitHub Actions irá construir e publicar a nova imagem no GHCR. Após até 5 minutos, o WatchTower detectará a nova imagem e atualizará automaticamente o container watchtower-example sem downtime.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feoaybq1vktiu6jxa9j8r.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feoaybq1vktiu6jxa9j8r.jpg" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🎉 Conclusão&lt;/p&gt;

&lt;p&gt;Integrar o WatchTower com o GitHub Container Registry oferece uma solução eficiente para manter suas aplicações Docker sempre atualizadas de forma automática. Essa automação não apenas economiza tempo, mas também garante que suas aplicações estejam sempre rodando as versões mais recentes e seguras. Além disso, combinando com ferramentas como o Traefik, você pode criar uma infraestrutura robusta e escalável, pronta para atender às demandas de ambientes de produção modernos.&lt;/p&gt;

&lt;p&gt;Experimente implementar essa integração no seu projeto e aproveite os benefícios da automação contínua!&lt;/p&gt;

&lt;p&gt;📚 Recursos Adicionais&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/containrrr/watchtower" rel="noopener noreferrer"&gt;WatchTower GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry" rel="noopener noreferrer"&gt;GitHub Container Registry Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker Compose Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;GitHub Actions Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>watchtower</category>
      <category>github</category>
    </item>
    <item>
      <title>Implementing DataLoader and Understanding Its Advantages Over Lookup</title>
      <dc:creator>Lucas Aguiar</dc:creator>
      <pubDate>Thu, 15 Aug 2024 00:48:20 +0000</pubDate>
      <link>https://dev.to/lusqua/implementing-dataloader-and-understanding-its-advantages-over-lookup-1phb</link>
      <guid>https://dev.to/lusqua/implementing-dataloader-and-understanding-its-advantages-over-lookup-1phb</guid>
      <description>&lt;p&gt;If you’ve been working with databases, especially in a GraphQL environment, you might have encountered the "N+1 problem." This issue occurs when your system makes multiple database calls to fetch related data, which can significantly slow down your application. Enter &lt;strong&gt;DataLoader&lt;/strong&gt;—a tool that’s here to save the day by batching and caching database requests, making your queries much more efficient.&lt;/p&gt;

&lt;p&gt;Let’s dive into how DataLoader works and see it in action with a practical example involving bank transactions and user details.&lt;/p&gt;

&lt;h4&gt;
  
  
  What is DataLoader?
&lt;/h4&gt;

&lt;p&gt;DataLoader is a utility designed to batch and cache requests for data. Instead of making multiple calls to fetch each piece of data separately, DataLoader groups these requests together, sends them in one go, and then caches the results for future use. This approach is particularly useful in reducing the number of database queries, thus speeding up your application.&lt;/p&gt;

&lt;h4&gt;
  
  
  How DataLoader Solves the N+1 Problem
&lt;/h4&gt;

&lt;p&gt;Consider a scenario where you have a list of banking transactions, and each transaction is associated with a user. Without DataLoader, you might end up querying the database for each user separately, leading to multiple (and often redundant) database queries. This is where DataLoader shines—it batches these requests together, so the database is queried only once per user.&lt;/p&gt;

&lt;h4&gt;
  
  
  Implementing DataLoader: A Practical Example
&lt;/h4&gt;

&lt;p&gt;Let’s imagine you’re building a banking application, and you need to list transactions along with the details of the users who made those transactions. Here’s how you can implement DataLoader to optimize this process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Setting Up the DataLoader:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;DataLoader&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dataloader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ObjectId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongodb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getCollection&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./mongodbConfig&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Your MongoDB configuration file&lt;/span&gt;

   &lt;span class="c1"&gt;// Batch function to fetch users by IDs&lt;/span&gt;
   &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usersInBatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;coll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;objIdIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;found&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;coll&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;objIdIds&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;};&lt;/span&gt;

   &lt;span class="c1"&gt;// Create a DataLoader instance for users&lt;/span&gt;
   &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userLoader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;usersInBatch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;usersInBatch&lt;/code&gt; is a batch loading function that fetches user details for multiple &lt;code&gt;userId&lt;/code&gt;s at once, and &lt;code&gt;userLoader&lt;/code&gt; is the DataLoader instance that leverages this function.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Using DataLoader in Your Application:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userLoader&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./dataLoaderConfig&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Your DataLoader configuration file&lt;/span&gt;

   &lt;span class="c1"&gt;// Function to get transaction details&lt;/span&gt;
   &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getTransactionDetails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userLoader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;userEmail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;};&lt;/span&gt;
   &lt;span class="p"&gt;};&lt;/span&gt;

   &lt;span class="c1"&gt;// Function to list transactions with user details&lt;/span&gt;
   &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;listTransactions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;coll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;transactions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;skip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawTransactions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;coll&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawTransactions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getTransactionDetails&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
   &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;getTransactionDetails&lt;/code&gt; function uses &lt;code&gt;userLoader&lt;/code&gt; to fetch user details by &lt;code&gt;userId&lt;/code&gt;, ensuring that each user is only fetched once per batch.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;listTransactions&lt;/code&gt; retrieves a page of transactions and maps over them to enrich each transaction with the relevant user details.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Why Use DataLoader?
&lt;/h4&gt;

&lt;p&gt;By using DataLoader, you reduce the number of database queries from potentially hundreds (or more) to just a few. This not only optimizes performance but also reduces load on your database, which can be crucial in high-traffic applications. Moreover, DataLoader’s built-in caching mechanism ensures that once a user’s data is fetched, it doesn’t need to be fetched again in the same request cycle, further speeding up your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;DataLoader is an invaluable tool when dealing with scenarios that involve repeated database lookups, particularly in applications with complex data relationships like the one we explored here. By batching and caching requests, it dramatically improves the efficiency and performance of your database interactions. Whether you’re dealing with banking transactions, product orders, or any other domain where related data is frequently accessed, DataLoader can help you keep your application running smoothly and efficiently.&lt;/p&gt;

&lt;p&gt;If you're facing performance issues due to repeated database queries, give DataLoader a try—your application (and your users) will thank you!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
