<?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>Forem: Guille Ojeda</title>
    <description>The latest articles on Forem by Guille Ojeda (@guilleojeda).</description>
    <link>https://forem.com/guilleojeda</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F844914%2F298727d8-0011-4e6e-b686-c7a114e16578.png</url>
      <title>Forem: Guille Ojeda</title>
      <link>https://forem.com/guilleojeda</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/guilleojeda"/>
    <language>en</language>
    <item>
      <title>Building Intelligent Agentic Applications with Amazon Bedrock and Nova</title>
      <dc:creator>Guille Ojeda</dc:creator>
      <pubDate>Fri, 07 Mar 2025 20:47:33 +0000</pubDate>
      <link>https://forem.com/aws-builders/building-intelligent-agentic-applications-with-amazon-bedrock-and-nova-3oif</link>
      <guid>https://forem.com/aws-builders/building-intelligent-agentic-applications-with-amazon-bedrock-and-nova-3oif</guid>
      <description>&lt;h2&gt;
  
  
  What Are Agentic AI Architectures?
&lt;/h2&gt;

&lt;p&gt;I won't waste your time with a long, fluffy introduction about how AI is changing the world. Let's get straight to the point: agentic AI architectures are fundamentally different from the prompt-response pattern you're probably used to with language models.&lt;/p&gt;

&lt;p&gt;In an agentic architecture, the AI doesn't just spit out a response to your input. Instead, it functions as an autonomous agent that breaks down complex tasks into steps, executes those steps by calling the right tools, and uses the results to inform subsequent actions. Think of it as the difference between asking someone a question and hiring them to do a job - the agent actually does work on your behalf rather than just answering.&lt;/p&gt;

&lt;p&gt;Amazon Bedrock and the Nova model family are AWS's offering in this space. Bedrock provides the managed infrastructure and orchestration, while Nova models serve as the intelligence. In this article we'll dig into how these technologies work together, the architectural patterns for implementing agentic systems, and the practical considerations for building them at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Amazon Bedrock and the Nova Model Family
&lt;/h2&gt;

&lt;p&gt;Amazon Bedrock is AWS's fully managed service for building generative AI applications. It provides a unified API for accessing foundation models, but it's not just a model gateway, it's a comprehensive platform for building, deploying, and running AI applications without managing infrastructure.&lt;/p&gt;

&lt;p&gt;The Amazon Nova family is AWS's proprietary set of foundation models, with several variants optimized for different use cases:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Context Window&lt;/th&gt;
&lt;th&gt;Multimodal?&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Pricing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nova Micro&lt;/td&gt;
&lt;td&gt;Text-only&lt;/td&gt;
&lt;td&gt;32K tokens&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Simple tasks, classification, high volume&lt;/td&gt;
&lt;td&gt;$0.000035/1K input tokens, $0.00014/1K output tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nova Lite&lt;/td&gt;
&lt;td&gt;Multimodal&lt;/td&gt;
&lt;td&gt;128K tokens&lt;/td&gt;
&lt;td&gt;Yes (text, image, video)&lt;/td&gt;
&lt;td&gt;Balanced performance, routine agent tasks&lt;/td&gt;
&lt;td&gt;$0.00006/1K input tokens, $0.00024/1K output tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nova Pro&lt;/td&gt;
&lt;td&gt;Multimodal&lt;/td&gt;
&lt;td&gt;Up to 300K tokens&lt;/td&gt;
&lt;td&gt;Yes (text, image, video)&lt;/td&gt;
&lt;td&gt;Complex reasoning, sophisticated agents&lt;/td&gt;
&lt;td&gt;$0.0008/1K input tokens, $0.0032/1K output tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What makes these models particularly suited for agentic applications? First, they're optimized for function calling: the ability to output structured JSON requests for external tools. Second, those large context windows allow agents to maintain extensive conversation history and detailed instructions. Third, the multimodal capabilities (in Lite and Pro) let agents process images and videos alongside text.&lt;/p&gt;

&lt;p&gt;Under the hood, Bedrock scales compute resources automatically based on demand. When your agent suddenly gets hit with a traffic spike, AWS provisions additional resources to maintain performance. There's no infrastructure for you to manage, just APIs to call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agentic Architectures: Beyond Simple Prompt-Response Systems
&lt;/h2&gt;

&lt;p&gt;So what exactly makes agentic architectures different from regular LLM applications? Let me break it down with a practical analogy.&lt;/p&gt;

&lt;p&gt;A traditional LLM application is like asking someone a question at an information desk: you expect them to answer based on what they know, but they won't leave their desk to do anything for you. An agentic architecture is more like having a personal assistant: they'll not only answer your questions, but also make phone calls, look up information, and take actions on your behalf.&lt;/p&gt;

&lt;p&gt;The foundation of this approach is what we call the Reason-Act-Observe loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Reason: The agent analyzes the current state and decides what to do next&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Act: It executes an action by calling an external tool/API&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Observe: It processes the result from that action&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Loop: Based on what it observed, it reasons again about the next step&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This cycle continues until the agent determines it has completed the task. It's similar to how you might approach a complex task: you don't solve problems in one leap, but through a series of steps, evaluating after each one.&lt;/p&gt;

&lt;p&gt;Here's how this translates to AWS implementations. When you build an agent on Bedrock, you're essentially defining what tools (AWS calls these "action groups") the agent can use, what data sources (knowledge bases) it can reference, and what instructions guide its behavior. The actual orchestration, deciding which tool to use when and chaining the steps together, is handled by Bedrock's agent runtime.&lt;/p&gt;

&lt;p&gt;This approach has clear advantages. An agent can handle requests like "Find me flights to New York next weekend, check the weather forecast, and suggest some hotels near Central Park", a request that would be impossible to fulfill in one shot. By breaking it into steps (search flights, check weather, find hotels), and calling APIs for each piece of data, the agent can assemble a comprehensive response.&lt;/p&gt;

&lt;p&gt;But this approach isn't without trade-offs. Agentic systems are more complex to configure, potentially slower (since multiple steps and API calls take time), and generally more expensive in terms of both token usage and compute costs. You're paying for the additional reasoning steps and API calls that happen behind the scenes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bedrock Agents: Building Blocks and Architecture
&lt;/h2&gt;

&lt;p&gt;A Bedrock Agent consists of several key components:&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;foundation model&lt;/strong&gt; is the brain of your agent. For complex agents, Amazon Nova Pro is typically the best choice with its 300K token context window and multimodal capabilities. For simpler tasks or cost-sensitive applications, Nova Lite (128K tokens) or even Nova Micro (32K tokens) might be sufficient.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;instructions&lt;/strong&gt; define what your agent does. This is effectively a system prompt that guides the agent's behavior. For example:&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="nx"&gt;You&lt;/span&gt; &lt;span class="nx"&gt;are&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;travel&lt;/span&gt; &lt;span class="nx"&gt;planning&lt;/span&gt; &lt;span class="nx"&gt;assistant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Your&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;help&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="nx"&gt;find&lt;/span&gt; &lt;span class="nx"&gt;flights&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;accommodations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;plan&lt;/span&gt; &lt;span class="nx"&gt;itineraries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;You&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="nx"&gt;access&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;flight&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="nx"&gt;APIs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hotel&lt;/span&gt; &lt;span class="nx"&gt;databases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;weather&lt;/span&gt; &lt;span class="nx"&gt;forecasts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Always&lt;/span&gt; &lt;span class="nx"&gt;confirm&lt;/span&gt; &lt;span class="nx"&gt;dates&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;locations&lt;/span&gt; &lt;span class="nx"&gt;before&lt;/span&gt; &lt;span class="nx"&gt;making&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt; &lt;span class="nx"&gt;bookings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;If&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;s request is ambiguous, ask clarifying questions.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Action Groups&lt;/strong&gt; (what other frameworks might call "tools") define what your agent can do in the world. Each action group contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A schema (OpenAPI or function schema) describing available actions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A Lambda function implementing those actions&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a flight search action might be defined with this schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;openapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.0.0"&lt;/span&gt;
&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FlightSearchAPI&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0"&lt;/span&gt;
&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/flights/search&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Search for flights&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Finds available flights between origin and destination on specified dates.&lt;/span&gt;
      &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;origin&lt;/span&gt;
          &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;query&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Origin airport code (e.g., "JFK")&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;destination&lt;/span&gt;
          &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;query&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Destination airport code (e.g., "LAX")&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;departDate&lt;/span&gt;
          &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;query&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Departure date (YYYY-MM-DD)&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;returnDate&lt;/span&gt;
          &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;query&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Return date for round trip (YYYY-MM-DD)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a Lambda function to implement it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Extract parameters from the event
&lt;/span&gt;    &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;parameters&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="n"&gt;origin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;origin&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;destination&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;destination&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;depart_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;departDate&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;return_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;returnDate&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# In a real implementation, you'd call your flight API
&lt;/span&gt;    &lt;span class="c1"&gt;# For this example, we'll return mock data
&lt;/span&gt;    &lt;span class="n"&gt;flights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;airline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Oceanic Airlines&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flightNumber&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OA815&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;departureTime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;08:15&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arrivalTime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;11:30&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;299.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;currency&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;airline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;United Airlines&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flightNumber&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UA456&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;departureTime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;13:45&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arrivalTime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;17:00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;349.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;currency&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USD&lt;/span&gt;&lt;span class="sh"&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;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flights&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;origin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;destination&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;departDate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;depart_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;returnDate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;return_date&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optional &lt;strong&gt;Knowledge Bases&lt;/strong&gt; connect your agent to external data. These use vector embeddings (typically generated with Amazon Titan Embeddings) to find relevant information in your data sources. For instance, if you have a knowledge base of travel guides and a user asks about "things to do in Barcelona," the agent can automatically retrieve and reference the Barcelona guide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt Templates&lt;/strong&gt; control how the agent processes information at different stages. There are four main templates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Pre-processing (validating user input)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Orchestration (driving the decision-making)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Knowledge Base (handling retrievals)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Post-processing (refining the final answer)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The power of Bedrock Agents lies in how these components work together. When a user sends a request, the agent:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Processes the user input&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enters an orchestration loop where it repeatedly:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* Decides what to do next (answer directly or use a tool)

* If using a tool, calls the corresponding Lambda

* Processes the result and decides on next steps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Delivers the final response once the task is complete&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All of this happens automatically, your code just calls &lt;code&gt;invoke_agent&lt;/code&gt;, and Bedrock handles the complex orchestration behind the scenes.&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 45,000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://newsletter.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Knowledge Bases and Retrieval-Augmented Generation
&lt;/h2&gt;

&lt;p&gt;One of the most powerful features of Bedrock Agents is their ability to tap into your data through knowledge bases. This integration enables retrieval-augmented generation (RAG), where the agent grounds its responses in specific documents or data sources.&lt;/p&gt;

&lt;p&gt;Setting up a knowledge base involves three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Prepare your data source. This could be documents in S3, a database, or another repository. Bedrock supports multiple file formats including PDFs, Word docs, text files, HTML, and more.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create the knowledge base configuration, specifying:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* The data source (e.g., an S3 bucket)

* An embedding model (e.g., Amazon Titan Embeddings)

* Chunk size and overlap for document splitting

* Metadata options for filtering
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Associate the knowledge base with your agent.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When a user asks a question, the agent might determine it needs external information. It then:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Formulates a search query based on the user's question&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sends this query to the knowledge base&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Receives relevant document chunks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Incorporates these chunks into its reasoning&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Generates a response grounded in this information&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There's a trade-off to consider with knowledge bases: adding retrieved content to prompts increases token count and therefore cost. A prompt that might normally be 500 tokens could easily grow to 2,000+ tokens with retrieved content. However, the improvement in answer quality is often worth it.&lt;/p&gt;

&lt;p&gt;The chunking strategy significantly impacts retrieval quality. If chunks are too large, they'll contain irrelevant information and waste tokens. If they're too small, they might lose important context. A good starting point is 300-500 token chunks with about 10% overlap, but you'll need to experiment based on your specific content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance and Cost Optimization
&lt;/h2&gt;

&lt;p&gt;Let's talk numbers: how much will this actually cost you, and how do you keep it reasonable?&lt;/p&gt;

&lt;p&gt;The cost of running agentic applications on Bedrock comes down to several factors:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Model Invocation Costs&lt;/strong&gt;: This is the primary expense. Each time the agent "thinks," it invokes the foundation model, which charges per token. For Nova models, input tokens (what you send to the model) are 8 times cheaper than output tokens (what it generates). You can view the prices on the &lt;a href="https://aws.amazon.com/bedrock/pricing/" rel="noopener noreferrer"&gt;official Bedrock pricing page&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tool Execution Costs&lt;/strong&gt;: Every tool the agent calls typically invokes a Lambda function and possibly other AWS services, each with their own costs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Knowledge Base Costs&lt;/strong&gt;: These include the initial vectorization of your data, storage of embeddings, and retrieval operations.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here are some strategies to optimize costs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use the right model for the job&lt;/strong&gt;. Nova Micro is vastly cheaper than Nova Pro, so consider using it for simpler tasks. You could even implement a cascading approach: try with Micro first, and only escalate to Pro for complex queries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optimize prompt sizes&lt;/strong&gt;. Keep your instructions concise, trim conversation history when possible, and only include relevant information. Every token costs money.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Take advantage of&lt;/strong&gt; &lt;a href="https://newsletter.simpleaws.dev/p/amazon-bedrock-prompt-caching" rel="noopener noreferrer"&gt;&lt;strong&gt;prompt caching&lt;/strong&gt;&lt;/a&gt;. Bedrock caches repeated portions of prompts (like instructions or tool definitions) and offers up to 90% discount on those cached tokens. This can significantly reduce costs for agents that have consistent patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For high volume, use provisioned throughput&lt;/strong&gt;. If you're consistently running many agent invocations, Provisioned Throughput offers lower per-token rates in exchange for a capacity commitment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitor token usage&lt;/strong&gt;. Set up CloudWatch alarms to alert you if usage spikes unexpectedly, which could indicate an issue with your agent's logic or a potential abuse.&lt;/p&gt;

&lt;p&gt;As for performance, agent orchestration adds latency because of the multiple steps involved. A simple query might take 2-3 seconds, while a complex one requiring multiple tool calls could take 10+ seconds. Be upfront with users about this latency, and consider implementing a streaming interface to show intermediate progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Implementation Patterns
&lt;/h2&gt;

&lt;p&gt;Beyond the basics, there are several advanced patterns that can enhance your agents' capabilities and efficiency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom Prompt Templates&lt;/strong&gt;: The default Bedrock templates work well, but customizing them gives you more control. For example, you might modify the orchestration template to include specific reasoning steps or decision criteria:&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="nx"&gt;Given&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;s request and available tools, determine the best course of action by:
1. Identifying the specific information or task the user is requesting
2. Checking if you already have all necessary information in the context
3. If not, selecting the appropriate tool or asking a clarifying question
4. Once you have all information, providing a concise answer

Remember:
- Only use tools when necessary, not for information already provided
- Always verify flight details before proceeding with any booking
- If multiple actions are needed, handle them one at a time
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Model Cascading&lt;/strong&gt;: You can implement a multi-tier approach where simple queries get handled by lightweight models and only complex ones escalate to more powerful models. This isn't built into Bedrock directly, but you can create a router function that analyzes incoming queries and dispatches them to different agents powered by different models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chain of Agents&lt;/strong&gt;: For complex workflows, you might create multiple specialized agents that work together. For example, a travel planning system might have separate agents for flight search, hotel recommendations, and itinerary creation. A controller coordinates between these agents, passing information between them as needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hybrid RAG Approaches&lt;/strong&gt;: While basic RAG works well, advanced implementations might combine multiple retrieval strategies. For instance, you could implement a system that first attempts semantic search, then falls back to keyword search if the results aren't satisfactory. This can be implemented by customizing your Lambda functions that process knowledge base results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integration with Human Workflows&lt;/strong&gt;: For high-stakes scenarios, consider integrating human review into the agent's workflow. The agent can handle routine cases autonomously but elevate complex or risky cases to human reviewers. This requires additional orchestration logic, typically implemented through Step Functions or a similar workflow service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security and Access Control
&lt;/h2&gt;

&lt;p&gt;Security is particularly important for agentic applications because they actively invoke services and access data. Getting this wrong means your agent could potentially do things you never intended.&lt;/p&gt;

&lt;p&gt;The cornerstone of Bedrock Agent security is IAM. Each agent operates with an IAM execution role that defines what AWS resources it can access. Follow the principle of least privilege rigidly - grant only the specific permissions needed for the agent's functions and nothing more.&lt;/p&gt;

&lt;p&gt;Here's an example IAM policy for an agent that only needs to call two specific Lambda functions:&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="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&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;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lambda:InvokeFunction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&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;"arn:aws:lambda:us-east-1:123456789012:function:FlightSearchFunction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:lambda:us-east-1:123456789012:function:HotelSearchFunction"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, apply resource-based policies on your Lambda functions to ensure they can only be invoked by your Bedrock Agent:&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="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&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;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&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;"Service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bedrock.amazonaws.com"&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;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lambda:InvokeFunction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:lambda:us-east-1:123456789012:function:FlightSearchFunction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&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;"StringEquals"&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;"AWS:SourceAccount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123456789012"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Lambda functions that access sensitive data or services, implement additional validation. Don't assume that because your agent is well-behaved, the data it passes to your functions will be well-formed or safe. Validate everything.&lt;/p&gt;

&lt;p&gt;If your agent processes personal or sensitive information, consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Using Bedrock Guardrails to filter inappropriate content&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implementing PII detection and masking in your Lambda functions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Encrypting sensitive data at rest and in transit&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Setting up comprehensive logging and auditing&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your agent acts on behalf of specific users, ensure user identity and permissions are properly propagated. One approach is to pass user tokens through the agent's session attributes and have your Lambda functions validate these tokens before accessing user-specific resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: The Future of Agentic Applications on AWS
&lt;/h2&gt;

&lt;p&gt;Agentic applications represent a significant step forward in what's possible with AI. By combining the reasoning capabilities of foundation models with the ability to take actions in the real world, these systems can handle complex tasks that would be impossible for traditional applications.&lt;/p&gt;

&lt;p&gt;Amazon Bedrock and the Nova model family provide a robust platform for building these applications. You get the benefit of managed infrastructure and powerful foundation models, while retaining the flexibility to integrate with your existing AWS services and data.&lt;/p&gt;

&lt;p&gt;The patterns we've explored in this article, from action groups and knowledge bases to security controls and cost optimizations, aren't just theoretical. They're being applied today in customer service, enterprise productivity, data analysis, and many other domains.&lt;/p&gt;

&lt;p&gt;As you start exploring this space, remember that building effective agents requires balancing several factors: technical capability, user experience, security, and cost. The most successful implementations are those that get this balance right for their specific use case.&lt;/p&gt;

&lt;p&gt;While the technology is powerful, it's not magic. Agents have limitations: they may sometimes misunderstand requests, take longer than expected to complete tasks, or struggle with highly complex workflows. Set realistic expectations with your users, and design your applications to gracefully handle these edge cases.&lt;/p&gt;

&lt;p&gt;Despite these challenges, the potential is enormous. As foundation models continue to improve and AWS enhances the Bedrock platform, the possibilities for intelligent, autonomous applications will only expand. The agents you build today are just the beginning of a new approach to software that's more capable, more contextual, and more helpful than ever before.&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 45,000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://newsletter.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real&lt;/strong&gt; scenarios and solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;why&lt;/strong&gt; behind the solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices&lt;/strong&gt; to improve them&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://newsletter.simpleaws.dev/subscribe?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Subscribe for free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to know more about me, you can find me &lt;a href="https://www.linkedin.com/in/guilleojeda/" rel="noopener noreferrer"&gt;on LinkedIn&lt;/a&gt; or at &lt;a href="https://www.guilleojeda.com?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;www.guilleojeda.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
    </item>
    <item>
      <title>Building Reliable Messaging Patterns in AWS with SQS and SNS</title>
      <dc:creator>Guille Ojeda</dc:creator>
      <pubDate>Fri, 20 Dec 2024 15:21:55 +0000</pubDate>
      <link>https://forem.com/aws-builders/building-reliable-messaging-patterns-in-aws-with-sqs-and-sns-3hoo</link>
      <guid>https://forem.com/aws-builders/building-reliable-messaging-patterns-in-aws-with-sqs-and-sns-3hoo</guid>
      <description>&lt;p&gt;Building distributed systems requires putting a lot of attention on communication between components. These components often need to exchange information asynchronously, and that's where message queues and pub/sub systems are the go-to solution. AWS provides two core services for this purpose: &lt;strong&gt;Simple Queue Service (SQS)&lt;/strong&gt; and &lt;strong&gt;Simple Notification Service (SNS)&lt;/strong&gt;. While these managed services handle the fundamental mechanics of message delivery, you need to understand how to configure them to build reliable distributed systems.&lt;/p&gt;

&lt;p&gt;This article explores those configuration details, as well as practical patterns for implementing reliable messaging using SQS and SNS. We'll examine how these services work together, talk about error handling strategies, and learn how to scale messaging infrastructure effectively.&lt;/p&gt;

&lt;p&gt;The examples in this article use Node.js, but the patterns apply to any programming language with an AWS SDK.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding AWS Messaging Services
&lt;/h2&gt;

&lt;p&gt;AWS messaging services solve different aspects of the distributed communication problem. &lt;strong&gt;SQS&lt;/strong&gt; provides managed message queues that enable point-to-point communication between components. When a producer sends a message to an SQS queue, that message will be delivered to &lt;strong&gt;exactly one consumer&lt;/strong&gt;. This guarantee makes SQS ideal for workload distribution and task processing.&lt;/p&gt;

&lt;p&gt;Here's how to create a basic SQS queue:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;standardQueueConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;QueueName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order-processing-queue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;MessageRetentionPeriod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1209600&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;ReceiveMessageWaitTimeSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;20&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;VisibilityTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;30&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration above defines how your queue is going to behave. Message retention period determines how long messages remain available if not processed, in this case 14 days. The receive message wait time enables long polling, reducing empty responses and unnecessary API calls. Visibility timeout specifies how long a message remains hidden during processing, preventing multiple consumers from processing the same message simultaneously.&lt;/p&gt;

&lt;p&gt;SQS offers two queue types: &lt;strong&gt;Standard and FIFO (First-In-First-Out)&lt;/strong&gt;. Standard queues provide "at-least-once" delivery and support nearly unlimited throughput, but messages may occasionally be delivered out of order or more than once. FIFO queues, on the other hand, guarantee exactly-once processing and strict message ordering, but with limited throughput - 3,000 messages per second with batching, or 300 without.&lt;/p&gt;

&lt;p&gt;FIFO queues require additional configuration:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fifoQueueConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;QueueName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order-processing-queue.fifo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;FifoQueue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;ContentBasedDeduplication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;MessageRetentionPeriod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1209600&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;ReceiveMessageWaitTimeSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;20&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;VisibilityTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;30&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The .fifo suffix in the queue name is mandatory for FIFO queues. Content-based deduplication automatically detects and removes duplicate messages based on their content, though you can also provide explicit deduplication IDs if needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SNS&lt;/strong&gt;, meanwhile, implements the &lt;strong&gt;publish-subscribe&lt;/strong&gt; pattern. Messages sent to an SNS topic are delivered to multiple subscribers simultaneously. This makes SNS ideal for broadcasting notifications, implementing event-driven architectures, and decoupling services. When a message arrives at an SNS topic, it fans out to all subscribed endpoints immediately.&lt;/p&gt;

&lt;p&gt;Creating an SNS topic involves specifying its basic attributes and any desired message filtering capabilities:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;topicConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order-events&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;KmsMasterKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alias/aws/sns&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;FilterPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order_created&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order_updated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order_cancelled&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Message filtering in SNS deserves special attention because it can significantly reduce unnecessary processing. Rather than forcing every subscriber to receive and filter messages themselves, SNS can filter messages at the service level based on message attributes:&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;// Message filtering configuration&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filterPolicy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order_created&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HIGH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;us-west-2&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subscriptionAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;FilterPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filterPolicy&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;When applied to a subscription, this filter ensures the subscriber only receives messages matching specific criteria. This filtering happens before message delivery, reducing both processing overhead and potential costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Reliable Queue Processing
&lt;/h2&gt;

&lt;p&gt;Processing messages reliably requires paying special attention to several aspects of the messaging lifecycle.&lt;/p&gt;

&lt;p&gt;First, let's look at a basic but reliable message processor:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processQueue&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;queueUrl&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;receiveParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;QueueUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;queueUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;MaxNumberOfMessages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;WaitTimeSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;MessageAttributeNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;All&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;try&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;data&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;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receiveMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;receiveParams&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;promise&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Messages&lt;/span&gt;&lt;span class="p"&gt;)&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="k"&gt;for &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;message&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Messages&lt;/span&gt;&lt;span class="p"&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;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;processMessageByType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;deleteMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queueUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReceiptHandle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Successfully processed message &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MessageId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error processing message &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MessageId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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="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;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error receiving messages:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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;p&gt;This implementation includes several important reliability features. Long polling reduces unnecessary API calls while ensuring timely message processing. Batch message processing improves throughput and reduces costs. Error handling at both the receive and process levels ensures that failures don't crash the processor. Messages are only deleted after successful processing, ensuring no message is lost due to processing failures.&lt;/p&gt;

&lt;p&gt;However, reliable message processing requires more than just careful implementation. We need to handle messages that consistently fail processing, implement proper monitoring, and ensure our system scales appropriately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Failed Messages with Dead Letter Queues
&lt;/h2&gt;

&lt;p&gt;Messages that can't be processed successfully after multiple attempts need special handling. &lt;strong&gt;Dead Letter Queues (DLQs)&lt;/strong&gt; provide a way to isolate these problematic messages for analysis and potential reprocessing. Here's how to implement a good DLQ strategy:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dlqConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;QueueName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order-processing-dlq&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;MessageRetentionPeriod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1209600&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mainQueueConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;QueueName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order-processing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;RedrivePolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;deadLetterTargetArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dlqArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;maxReceiveCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&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;p&gt;The redrive policy automatically moves messages to the DLQ after multiple failed processing attempts. This prevents infinite processing loops while preserving failed messages for analysis. The maxReceiveCount parameter determines how many processing attempts are allowed before a message moves to the DLQ.&lt;/p&gt;

&lt;p&gt;Processing messages from a DLQ requires a couple of changes:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processDLQ&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;dlqUrl&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;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;QueueUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dlqUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;MaxNumberOfMessages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;WaitTimeSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;MessageAttributeNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;All&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;try&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;data&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;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receiveMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;promise&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Messages&lt;/span&gt;&lt;span class="p"&gt;)&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="k"&gt;for &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;message&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Messages&lt;/span&gt;&lt;span class="p"&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;const&lt;/span&gt; &lt;span class="nx"&gt;failureAnalysis&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;analyzeFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&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;failureAnalysis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isRecoverable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;returnToMainQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;storeFailedMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;deleteMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dlqUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReceiptHandle&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;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error processing DLQ message:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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="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;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error receiving DLQ messages:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;analyzeFailure&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;message&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;attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MessageAttributes&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;messageAge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SentTimestamp&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;failureCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ApproximateReceiveCount&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="na"&gt;isRecoverable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;messageAge&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;86400000&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;failureCount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;failureReason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;determineFailureReason&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&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;p&gt;This implementation analyzes failed messages to determine if they're recoverable based on their age and failure count. Recoverable messages can be returned to the main queue for reprocessing, while permanently failed messages are stored for further analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring and Observability
&lt;/h2&gt;

&lt;p&gt;A reliable messaging system requires good monitoring to detect and respond to issues before they impact your applications. &lt;strong&gt;Amazon CloudWatch&lt;/strong&gt; provides basic metrics for both SQS and SNS, but effective monitoring requires understanding which metrics actually matter and how to interpret them.&lt;/p&gt;

&lt;p&gt;For SQS queues, the ApproximateNumberOfMessages metric indicates how many messages are available for retrieval. However, this number alone doesn't tell the whole story. You also need to monitor ApproximateNumberOfMessagesNotVisible, which shows messages currently being processed, and ApproximateAgeOfOldestMessage, which can indicate processing backlogs or stalled consumers.&lt;/p&gt;

&lt;p&gt;Here's how to set up basic queue monitoring:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setupQueueMonitoring&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;queueUrl&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;alarmConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;AlarmName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;QueueMessageAge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;AlarmDescription&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alert when messages are getting old&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;MetricName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ApproximateAgeOfOldestMessage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AWS/SQS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Dimensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
            &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;QueueName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getQueueNameFromUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queueUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="na"&gt;Period&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;EvaluationPeriods&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;Threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GreaterThanThreshold&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Statistic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Maximum&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cloudwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;putMetricAlarm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;alarmConfig&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;promise&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;This configuration alerts you when messages remain unprocessed for more than an hour, which might indicate processing issues. However, CloudWatch metrics alone often don't provide enough visibility into message processing. Custom metrics can provide deeper insights into your system's behavior:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recordCustomMetrics&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;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;processingResult&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;metrics&lt;/span&gt; &lt;span class="o"&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;MetricName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MessageProcessingTime&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;processingResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Milliseconds&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;Dimensions&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;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MessageType&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messageType&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Environment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&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="nx"&gt;ENVIRONMENT&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="na"&gt;Timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&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;await&lt;/span&gt; &lt;span class="nx"&gt;cloudwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;putMetricData&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CustomMessageProcessing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;MetricData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metrics&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;promise&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;These custom metrics track processing time by message type, helping you identify performance patterns and potential bottlenecks. You might discover that certain message types consistently take longer to process or fail more frequently than others.&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 45,000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://newsletter.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security and Access Control
&lt;/h2&gt;

&lt;p&gt;Security in messaging systems isn't just authentication and authorization. It also involves encryption, access control, and secure cross-account communication. Both SQS and SNS support server-side encryption using AWS KMS, which should be enabled for sensitive data (or for any data, really):&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setupQueueEncryption&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;queueUrl&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;attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;QueueUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;queueUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;KmsMasterKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alias/aws/sqs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;Policy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2012-10-17&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
                    &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Deny&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SQS:*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;queueArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws:SecureTransport&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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="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;await&lt;/span&gt; &lt;span class="nx"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setQueueAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;promise&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;Always remember the principle of least privilege. Producer services should only have permission to send messages, while consumer services should only have permission to receive and delete messages:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;producerPolicy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2012-10-17&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Allow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sqs:SendMessage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sqs:GetQueueUrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;queueArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;ArnLike&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws:SourceArn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;producerServiceArn&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cross-account messaging adds a bit of complexity. When services in different AWS accounts need to communicate, you must configure both the sender's IAM permissions and the receiving queue's resource policy:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crossAccountQueuePolicy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2012-10-17&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Allow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;AWS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sourceAccountArn&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sqs:SendMessage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;queueArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;StringEquals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws:SourceAccount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sourceAccountId&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Messaging Patterns
&lt;/h2&gt;

&lt;p&gt;There will come a time when what I've showed above isn't enough for your system. Let's explore some advanced patterns that address common distributed system challenges.&lt;/p&gt;

&lt;p&gt;Message batching can significantly improve throughput and reduce costs. However, implementing batching requires you to be mindful of how you handle failures and timeouts:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;batchProcessor&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;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;processor&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;messageGroups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&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;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MessageAttributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StringValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&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;groups&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;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;groupMessages&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messageGroups&lt;/span&gt;&lt;span class="p"&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;groupMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;batchDeleteMessages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queueUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;groupMessages&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;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error processing message group &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Handle partial batch failures by deleting successful messages&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;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;partialSuccess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;batchDeleteMessages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queueUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;successfulMessages&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="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;When messages must be processed in order, such as in event sourcing systems, you need to implement ordering guarantees even with standard queues:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orderDependentProcessor&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;queueUrl&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;messageCache&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;Map&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;processingOrder&lt;/span&gt; &lt;span class="o"&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;processMessageIfReady&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;message&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;sequenceNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MessageAttributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SequenceNumber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StringValue&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;sequenceNumber&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;processingOrder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;messageCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sequenceNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;processMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;processingOrder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sequenceNumber&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;nextSequence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sequenceNumber&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="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messageCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextSequence&lt;/span&gt;&lt;span class="p"&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;nextMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;messageCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextSequence&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;messageCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextSequence&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;processMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;processingOrder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextSequence&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;nextSequence&lt;/span&gt;&lt;span class="o"&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Circuit breakers protect downstream services from cascade failures. In messaging systems, circuit breakers can prevent queue processors from overwhelming struggling dependencies, and will isolate a failure preventing it from bringing down the entire system:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MessageProcessorCircuitBreaker&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;failureThreshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resetTimeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;failureCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;failureThreshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;failureThreshold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resetTimeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resetTimeout&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastFailureTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CLOSED&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;async&lt;/span&gt; &lt;span class="nf"&gt;processMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;processor&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OPEN&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastFailureTime&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resetTimeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HALF_OPEN&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Circuit breaker is OPEN&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="k"&gt;try&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;result&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;processor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HALF_OPEN&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CLOSED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;failureCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&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;result&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;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handleFailure&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;error&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;handleFailure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;failureCount&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastFailureTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;failureCount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;failureThreshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OPEN&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance and Cost Optimization
&lt;/h2&gt;

&lt;p&gt;Here's where we talk about service limits, implementing efficient processing patterns, and managing costs effectively. Standard SQS queues offer virtually unlimited throughput, while FIFO queues have specific limits that you need to be mindful of:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scalingConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;standardQueue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;batchSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;concurrentExecutions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;processingTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;fifoQueue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;maxThroughput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;batchSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;messageGroupId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orderProcessing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;deduplicationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;v4&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;processingTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&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;Cost optimization mostly involves balancing message retention, polling frequency, and batch processing. Long polling reduces API calls and associated costs:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;costOptimizedReceive&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;queueUrl&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;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;QueueUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;queueUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;MaxNumberOfMessages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;WaitTimeSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;AttributeNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SentTimestamp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;MessageAttributeNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MessageType&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;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receiveMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;promise&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;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building reliable messaging systems isn't just creating SQS queues and SNS topics and calling it a day. It requires understanding how the services work, how to configure them, and how to use them effectively in distributed systems. Proper error handling, monitoring, and security are just a few of the things you need to be mindful of. The patterns and practices discussed here serve as a foundation for building robust messaging systems, but it's left as an exercise to the reader to adapt them to your specific requirements and constraints.&lt;/p&gt;

&lt;p&gt;Remember that reliability in distributed systems isn't about preventing all failures. It's about handling failures gracefully when they occur. Testing your messaging patterns under different failure conditions will help ensure your system remains reliable even when components fail or become overloaded.&lt;/p&gt;

&lt;p&gt;As with any system, how your components communicate should evolve with your requirements. Start with simple patterns and add complexity only when required. Monitor your system's behavior, understand your traffic patterns, and adjust your implementation accordingly.&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 45,000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://newsletter.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real&lt;/strong&gt; scenarios and solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;why&lt;/strong&gt; behind the solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices&lt;/strong&gt; to improve them&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://newsletter.simpleaws.dev/subscribe?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Subscribe for free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to know more about me, you can find me &lt;a href="https://www.linkedin.com/in/guilleojeda/" rel="noopener noreferrer"&gt;on LinkedIn&lt;/a&gt; or at &lt;a href="https://www.guilleojeda.com?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;www.guilleojeda.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>distributedsystems</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Failover in Amazon RDS Multi-AZ Architectures</title>
      <dc:creator>Guille Ojeda</dc:creator>
      <pubDate>Wed, 18 Dec 2024 23:23:44 +0000</pubDate>
      <link>https://forem.com/aws-builders/failover-in-amazon-rds-multi-az-architectures-4g6h</link>
      <guid>https://forem.com/aws-builders/failover-in-amazon-rds-multi-az-architectures-4g6h</guid>
      <description>&lt;p&gt;Database failures are inevitable. Even with the most reliable hardware and software, something will eventually break. AWS RDS Multi-AZ deployments promise to handle these failures gracefully, automatically failing over to a standby database when problems occur. But like many things in distributed systems, the reality is more complex than the marketing suggests.&lt;/p&gt;

&lt;p&gt;Let's dive deep into how RDS Multi-AZ really works, what happens during failover, and how to design your applications to handle it properly. Understanding these internals will help you build more reliable applications and troubleshoot issues when they occur.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Amazon RDS Architecture
&lt;/h2&gt;

&lt;p&gt;Before we can understand Multi-AZ, we need to understand how RDS works under the hood. RDS is a complex distributed system that manages databases. When you create an RDS instance, you're actually getting several pieces working together.&lt;/p&gt;

&lt;p&gt;At the core is an EC2 instance running your chosen database engine. This instance has EBS volumes attached to it for storage, and it's connected to your VPC through Elastic Network Interfaces. There's also a control plane running in AWS's infrastructure that manages everything from automated backups to failover decisions.&lt;/p&gt;

&lt;p&gt;This separation between the control plane and data plane is crucial. The control plane runs in AWS's infrastructure, independently of your database instances. This means it can continue making decisions and taking actions even when your database instances are having problems. That's particularly important during failover scenarios.&lt;/p&gt;

&lt;p&gt;The storage layer is equally important. Your data lives on EBS volumes, which operate independently from the EC2 instance running your database. This separation of compute and storage enables some of RDS's coolest features, including the storage-level replication that makes Multi-AZ work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Availability Zones in AWS
&lt;/h2&gt;

&lt;p&gt;AWS documentation often describes Availability Zones as "physically separated locations with independent power, networking, and cooling." That's true, but the key point is that they're engineered for complete failure isolation from other AZs.&lt;/p&gt;

&lt;p&gt;AWS runs dedicated fiber connections between AZs in a region, engineered for consistent low latency. These connections typically maintain sub-millisecond latency between AZs, with multiple redundant paths. This high-bandwidth, low-latency connectivity is what makes synchronous replication practical.&lt;/p&gt;

&lt;p&gt;The network between AZs isn't part of the public internet. It's a dedicated network owned and operated by AWS, with quality of service controls that prioritize critical traffic like database replication. This matters because replication performance directly impacts how quickly your database can commit transactions in Multi-AZ deployments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-AZ Approaches in Amazon RDS
&lt;/h2&gt;

&lt;p&gt;RDS actually offers two different types of Multi-AZ deployments, and the differences matter. Traditional Multi-AZ deployments, which we'll focus on first, use a single primary instance with a standby replica. The newer Multi-AZ DB clusters use a primary instance with two readable standbys. The key difference isn't really the number of standbys, but how replication works.&lt;/p&gt;

&lt;p&gt;In traditional Multi-AZ, replication happens at the storage level. When your database writes to disk, that write is synchronously replicated to the standby's EBS volumes before being acknowledged. The standby database instance runs in recovery mode, continuously applying changes it sees in the storage layer.&lt;/p&gt;

&lt;p&gt;Multi-AZ DB clusters work differently, using the database engine's native replication. This means the standbys can serve read traffic, and it means replication has different performance characteristics and failure modes. The choice between these approaches depends on your specific needs for read scaling and consistency.&lt;/p&gt;

&lt;h2&gt;
  
  
  How RDS Multi-AZ Instance Replication Works
&lt;/h2&gt;

&lt;p&gt;When you write data to a Multi-AZ database, several things happen behind the scenes. First, your write operation arrives at the primary instance. The database engine processes it and writes to its local EBS volume. But before acknowledging the write back to your application, that write must be replicated.&lt;/p&gt;

&lt;p&gt;The replication process is handled by EBS, not the database engine. EBS synchronously copies each 16KB block that changes to the standby's EBS volumes. When a write occurs, EBS maintains a replication queue for changed blocks. Each block is checksummed and tracked to ensure consistency between volumes. If the queue starts growing too large, RDS will throttle writes to prevent the standby from falling too far behind.&lt;/p&gt;

&lt;p&gt;Behind the scenes, EBS also performs continuous consistency checking between volumes. If it detects inconsistent blocks, it will automatically repair them in the background. This process ensures that the standby's storage is truly a consistent copy of the primary, which is crucial for clean failovers.&lt;/p&gt;

&lt;p&gt;Only after both the primary and standby volumes have persisted the changes will the write be acknowledged. This ensures zero data loss if a failover occurs, but it also adds latency to every write operation.&lt;/p&gt;

&lt;p&gt;The standby instance runs in recovery mode, continuously monitoring its storage for changes and applying them to its internal state. This means it's ready to take over quickly if needed, but it can't serve queries or accept connections while it's in recovery mode.&lt;/p&gt;

&lt;p&gt;The replication process adds latency to every write operation. In typical scenarios, you'll see an additional 0.5-1ms for same-AZ writes and 1-2ms for cross-AZ writes. Large writes can take longer, sometimes adding 2-5ms of latency. This might seem small, but it can add up in write-heavy workloads.&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 45,000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://newsletter.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Anatomy of an RDS Failover
&lt;/h2&gt;

&lt;p&gt;A failover in RDS isn't a single operation, but a complex sequence of events that happens in several phases. When RDS detects a problem with the primary instance, it doesn't immediately fail over. Instead, it goes through a careful validation process to ensure the failover will succeed.&lt;/p&gt;

&lt;p&gt;The detection phase involves multiple health checks. RDS monitors EC2 status checks, EBS volume health, network connectivity, and replication status. It uses a complex decision matrix to determine whether a failure has actually occurred and whether failover is the appropriate response. This process typically takes between 0 and 10 seconds.&lt;/p&gt;

&lt;p&gt;Once RDS decides to fail over, it enters the validation phase. It verifies that the standby is healthy, that replication is current, and that all network paths are working. This includes checking storage consistency and ensuring the standby database can actually take over. This typically takes another 5-15 seconds.&lt;/p&gt;

&lt;p&gt;The actual failover begins with DNS changes. RDS updates the endpoint's CNAME record to point to the standby instance and adjusts the TTL to 5 seconds to speed up propagation. This process, including propagation time, typically takes 30-60 seconds.&lt;/p&gt;

&lt;p&gt;Meanwhile, the promotion phase begins. The standby database stops recovery mode, replays any remaining transactions from its storage, and starts accepting connections. This process typically takes 15-30 seconds, running in parallel with DNS propagation.&lt;/p&gt;

&lt;p&gt;Finally, RDS begins provisioning a new standby in the background. This doesn't affect database availability, but it's critical for maintaining high availability for future failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Applications That Handle RDS Failover
&lt;/h2&gt;

&lt;p&gt;Application design for Multi-AZ isn't just about handling database connection failures. You need to think about transaction retry logic, connection pooling, and how your application behaves during the transition period. Here's a Python example that illustrates some key concepts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pymysql&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;contextmanager&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RDSConnectionManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;host&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;database&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@contextmanager&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_create_connection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;pymysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_should_retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&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="c1"&gt;# Basic backoff
&lt;/span&gt;                &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_create_connection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt;
        &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_create_connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pymysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_should_retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Add logic to determine if error is retryable
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code demonstrates connection handling, but real applications need more sophisticated retry logic and connection pooling. Your application should handle various types of errors. Network timeouts might occur during the DNS switch. Transactions might be rolled back during the promotion phase. Connections might fail with various errors depending on exactly when and how they fail. Each of these scenarios needs appropriate handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring and Troubleshooting
&lt;/h2&gt;

&lt;p&gt;Effective monitoring of Multi-AZ deployments requires watching several CloudWatch metrics. &lt;code&gt;ReplicaLag&lt;/code&gt; tells you how far behind the standby is. &lt;code&gt;WriteIOPS&lt;/code&gt; and &lt;code&gt;WriteLatency&lt;/code&gt; help you understand replication performance. &lt;code&gt;ReadIOPS&lt;/code&gt; and &lt;code&gt;ReadLatency&lt;/code&gt; on the primary help you understand the workload.&lt;/p&gt;

&lt;p&gt;But raw metrics aren't enough. You need to understand how these metrics relate to each other and what patterns indicate problems. High &lt;code&gt;WriteLatency&lt;/code&gt; combined with increasing &lt;code&gt;ReplicaLag&lt;/code&gt; might indicate replication problems. High &lt;code&gt;CPUUtilization&lt;/code&gt; might explain increased &lt;code&gt;ReplicaLag&lt;/code&gt;. The relationships between metrics often tell you more than individual metrics alone.&lt;/p&gt;

&lt;p&gt;CloudWatch alarms should monitor for both immediate problems and trending issues. A spike in &lt;code&gt;ReplicaLag&lt;/code&gt; needs immediate attention, but gradually increasing &lt;code&gt;WriteLatency&lt;/code&gt; might indicate growing problems that need addressing before they cause failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Configurations and Edge Cases
&lt;/h2&gt;

&lt;p&gt;Multi-AZ works with various database engines, but the details vary. MySQL and PostgreSQL handle recovery mode differently, which affects failover timing. Oracle has its own nuances around transaction replay. Understanding these engine-specific details helps you design better applications.&lt;/p&gt;

&lt;p&gt;Parameter groups also affect Multi-AZ behavior. Settings that control durability and consistency can impact replication performance. Memory settings affect how quickly the standby can catch up after falling behind. Network timeout settings influence how quickly failures are detected.&lt;/p&gt;

&lt;p&gt;Edge cases are particularly important to understand. What happens if both AZs have connectivity issues? How does RDS handle simultaneous instance and storage failures? What if DNS propagation is delayed? These scenarios are rare but understanding them helps you build more resilient systems. Note that this doesn't mean you need to ensure your application can handle these scenarios. Not doing anything is a valid response, but only if you understand the risk first.&lt;/p&gt;

&lt;p&gt;Through this deep dive into RDS Multi-AZ, we've seen that while AWS handles much of the complexity, understanding the underlying mechanics helps you build better applications. From the basic architecture to complex failure scenarios, each aspect of Multi-AZ deployments has implications for your application's reliability and performance. So, now that you understand how that works in RDS, go build!&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 45,000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://newsletter.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real&lt;/strong&gt; scenarios and solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;why&lt;/strong&gt; behind the solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices&lt;/strong&gt; to improve them&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://newsletter.simpleaws.dev/subscribe?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Subscribe for free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to know more about me, you can find me &lt;a href="https://www.linkedin.com/in/guilleojeda/" rel="noopener noreferrer"&gt;on LinkedIn&lt;/a&gt; or at &lt;a href="https://www.guilleojeda.com?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;www.guilleojeda.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>database</category>
    </item>
    <item>
      <title>Relational Databases on AWS: Comparing RDS and Aurora</title>
      <dc:creator>Guille Ojeda</dc:creator>
      <pubDate>Tue, 23 Apr 2024 23:41:48 +0000</pubDate>
      <link>https://forem.com/aws-builders/relational-databases-on-aws-comparing-rds-and-aurora-581f</link>
      <guid>https://forem.com/aws-builders/relational-databases-on-aws-comparing-rds-and-aurora-581f</guid>
      <description>&lt;p&gt;There are two managed relational database services in AWS: Amazon Relational Database Service (RDS) and Amazon Aurora. Both provide the benefits of a fully managed database solution, but they have distinct features and use cases.&lt;/p&gt;

&lt;p&gt;In this article, we'll explore the key features and capabilities of RDS and Aurora, compare their differences, and provide guidance on choosing the right service for your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Amazon RDS (Relational Database Service)
&lt;/h2&gt;

&lt;p&gt;Let's start by taking a closer look at &lt;strong&gt;Amazon RDS&lt;/strong&gt;, AWS's fully managed relational database service. RDS makes it easy to set up, operate, and scale a relational database in the cloud, supporting a wide range of database engines.&lt;/p&gt;

&lt;p&gt;RDS Key Features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Fully managed database service&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Supports multiple database engines: MySQL, PostgreSQL, Oracle, SQL Server, MariaDB&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automatic backups and point-in-time recovery&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Multi-AZ deployments for high availability&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Read replicas for read scalability&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Vertical and horizontal scaling options&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RDS Instance Types and Storage:&lt;/p&gt;

&lt;p&gt;RDS offers a variety of instance types optimized for different workloads and performance requirements. Instance types range from small burstable instances to large memory-optimized instances.&lt;/p&gt;

&lt;p&gt;For storage, RDS provides three options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;General Purpose (SSD): Balanced performance for a wide range of workloads&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provisioned IOPS (SSD): High-performance storage for I/O-intensive workloads&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Magnetic: Cost-effective storage for infrequently accessed data&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pricing:&lt;/p&gt;

&lt;p&gt;With RDS, you pay for the database instance hours, storage, I/O requests, and data transfer. RDS pricing varies based on the database engine, instance type, storage type, and region.&lt;/p&gt;

&lt;h3&gt;
  
  
  RDS Backup and Restore
&lt;/h3&gt;

&lt;p&gt;One of the key benefits of using RDS is the automated backup and restore capabilities. RDS provides two types of backups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Automated Backups: RDS automatically takes daily snapshots of your database, allowing you to restore to any point in time within the retention period (up to 35 days).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Manual Snapshots: You can manually create database snapshots at any time, which are stored until you explicitly delete them.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To restore a database from a backup, you simply create a new RDS instance and specify the backup to use. RDS handles the rest, creating a new instance with the restored data.&lt;/p&gt;

&lt;p&gt;Point-in-time recovery (PITR) is another powerful feature of RDS. With PITR, you can restore your database to any point in time within the backup retention period, down to the second. This is particularly useful for recovering from accidental data modifications or deletions.&lt;/p&gt;

&lt;h3&gt;
  
  
  RDS High Availability and Failover
&lt;/h3&gt;

&lt;p&gt;High availability is crucial for many applications, and RDS provides several options to ensure your database remains available in the event of a failure.&lt;/p&gt;

&lt;p&gt;Multi-AZ Deployments:&lt;/p&gt;

&lt;p&gt;With a Multi-AZ deployment, RDS automatically provisions and maintains a synchronous standby replica in a different Availability Zone (AZ). If the primary instance fails, RDS automatically fails over to the standby, minimizing downtime.&lt;/p&gt;

&lt;p&gt;Multi-AZ deployments provide enhanced durability and fault tolerance, with failover typically completing within a minute or two. This is ideal for production workloads that require high availability.&lt;/p&gt;

&lt;p&gt;Read Replicas:&lt;/p&gt;

&lt;p&gt;Read replicas are separate database instances that are asynchronously replicated from the primary instance. They are used to offload read traffic from the primary instance and improve read scalability.&lt;/p&gt;

&lt;p&gt;You can create up to 5 read replicas per primary instance, within the same region or across different regions. Read replicas can be promoted to standalone instances if needed, providing a way to create independent databases for specific use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Amazon Aurora
&lt;/h2&gt;

&lt;p&gt;Amazon Aurora is a fully managed relational database service that is compatible with MySQL and PostgreSQL. It offers the simplicity and cost-effectiveness of open-source databases with the performance and availability of commercial databases.&lt;/p&gt;

&lt;p&gt;Aurora Key Features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;MySQL and PostgreSQL compatible&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;High-performance storage and caching&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Auto-scaling of read replicas&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Serverless option for automatic scaling&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Global database for multi-region deployments&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Continuous backups and point-in-time restore&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aurora Storage and Replication:&lt;/p&gt;

&lt;p&gt;Aurora uses a distributed, fault-tolerant, and self-healing storage system that automatically scales up to 128 TB per database instance. It replicates data across multiple AZs, providing high durability and availability.&lt;/p&gt;

&lt;p&gt;Aurora's storage is designed for fast, consistent performance. It uses a multi-tier caching architecture that includes an in-memory cache, a buffer pool, and a storage cache, reducing the need for disk I/O and improving performance.&lt;/p&gt;

&lt;p&gt;Pricing:&lt;/p&gt;

&lt;p&gt;With Aurora, you pay for the database instance hours, storage, I/O requests, and data transfer. Aurora pricing varies based on the database engine (MySQL or PostgreSQL), instance type, and region.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aurora Performance and Scalability
&lt;/h3&gt;

&lt;p&gt;One of the key advantages of Aurora is its high-performance storage and caching architecture. Aurora can deliver up to 5X the throughput of standard MySQL and 3X the throughput of standard PostgreSQL, without requiring any changes to your application code.&lt;/p&gt;

&lt;p&gt;Auto-scaling Read Replicas:&lt;/p&gt;

&lt;p&gt;Aurora automatically scales read replicas based on the workload, ensuring your database can handle read-heavy traffic patterns. As the read traffic increases, Aurora seamlessly adds new read replicas to the cluster, distributing the load across multiple instances.&lt;/p&gt;

&lt;p&gt;Aurora Serverless:&lt;/p&gt;

&lt;p&gt;For applications with unpredictable or intermittent workloads, Aurora Serverless provides a fully managed, auto-scaling configuration for Aurora MySQL and PostgreSQL. With Aurora Serverless, your database automatically starts up, shuts down, and scales capacity based on your application's needs.&lt;/p&gt;

&lt;p&gt;This is particularly useful for development and testing environments, or applications with variable traffic patterns, as it eliminates the need to manage database capacity manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aurora Backup and Restore
&lt;/h3&gt;

&lt;p&gt;Like RDS, Aurora provides automated continuous backups and point-in-time restore capabilities. However, Aurora takes it a step further with some additional features.&lt;/p&gt;

&lt;p&gt;Continuous Backups:&lt;/p&gt;

&lt;p&gt;Aurora automatically takes incremental backups of your database, continuously and transparently, with no impact on performance. These backups are stored in Amazon S3, providing 11 9's of durability.&lt;/p&gt;

&lt;p&gt;Backup Retention:&lt;/p&gt;

&lt;p&gt;Aurora backups are retained for a default period of 1 day, but you can configure this up to 35 days. Backups are automatically deleted when the retention period expires, or when the DB cluster is deleted.&lt;/p&gt;

&lt;p&gt;Point-in-time Restore:&lt;/p&gt;

&lt;p&gt;With Aurora, you can restore your database to any point in time within the backup retention period, down to the second. This is similar to RDS PITR, but with the added benefit of Aurora's distributed storage architecture, which enables faster restores.&lt;/p&gt;

&lt;p&gt;Database Cloning:&lt;/p&gt;

&lt;p&gt;Aurora allows you to create a new database cluster from an existing one, effectively "cloning" the database. This is useful for creating test or development environments, or for performing analytics on a copy of your production data without impacting the live database.&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 4000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=hashnode"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  RDS vs Aurora: Key Differences and Use Cases
&lt;/h2&gt;

&lt;p&gt;Now that we've explored the key features and capabilities of RDS and Aurora, let's compare them side by side.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;RDS&lt;/th&gt;
&lt;th&gt;Aurora&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database Engines&lt;/td&gt;
&lt;td&gt;MySQL, PostgreSQL, Oracle, SQL Server, MariaDB&lt;/td&gt;
&lt;td&gt;MySQL, PostgreSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;Good performance for general-purpose workloads&lt;/td&gt;
&lt;td&gt;High-performance storage and caching, optimized for read-heavy workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scalability&lt;/td&gt;
&lt;td&gt;Vertical and horizontal scaling, read replicas&lt;/td&gt;
&lt;td&gt;Auto-scaling read replicas, Aurora Serverless for automatic scaling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Availability&lt;/td&gt;
&lt;td&gt;Multi-AZ deployments for high availability&lt;/td&gt;
&lt;td&gt;Multi-AZ storage, Global Database for multi-region deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backup and Restore&lt;/td&gt;
&lt;td&gt;Automated backups, manual snapshots, point-in-time recovery&lt;/td&gt;
&lt;td&gt;Continuous incremental backups, point-in-time restore, database cloning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compatibility&lt;/td&gt;
&lt;td&gt;Wide range of database engines, easy migration&lt;/td&gt;
&lt;td&gt;MySQL and PostgreSQL compatible, requires migration effort&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Cost-effective for general-purpose workloads&lt;/td&gt;
&lt;td&gt;Higher cost, but better performance and scalability for demanding workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use Cases for RDS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Applications with moderate performance and scalability requirements&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Workloads that require a specific database engine (e.g., Oracle, SQL Server)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Migrating existing on-premises databases to the cloud&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Development and testing environments&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use Cases for Aurora:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Applications with high-performance and high-scalability requirements&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Read-heavy workloads that can benefit from Aurora's caching and auto-scaling capabilities&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Applications with unpredictable or variable traffic patterns (using Aurora Serverless)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Global applications that require multi-region database deployments&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Choosing the Right Relational Database Service
&lt;/h2&gt;

&lt;p&gt;Choosing between RDS and Aurora depends on your specific application requirements and workload characteristics. Here are some key factors to consider:&lt;/p&gt;

&lt;p&gt;Performance and Scalability:&lt;/p&gt;

&lt;p&gt;If your application demands high performance and scalability, particularly for read-heavy workloads, Aurora is the better choice. Its high-performance storage and caching architecture, along with auto-scaling read replicas, make it well-suited for demanding applications.&lt;/p&gt;

&lt;p&gt;Database Engine Compatibility:&lt;/p&gt;

&lt;p&gt;If your application requires a specific database engine, such as Oracle or SQL Server, RDS is the way to go. RDS supports a wide range of database engines, making it easier to migrate existing applications to the cloud.&lt;/p&gt;

&lt;p&gt;Cost Considerations:&lt;/p&gt;

&lt;p&gt;For general-purpose workloads with moderate performance requirements, RDS is more cost-effective than Aurora. However, if your application requires the high performance and scalability of Aurora, the additional cost may be justified.&lt;/p&gt;

&lt;p&gt;Existing Skills and Expertise:&lt;/p&gt;

&lt;p&gt;If your team is already familiar with MySQL or PostgreSQL, both RDS and Aurora are good choices. However, if you have expertise with a specific database engine supported by RDS, such as Oracle or SQL Server, that may be a deciding factor.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Use RDS
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Migrating an existing on-premises database to the cloud&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Applications with moderate performance and scalability requirements&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Workloads that require a specific database engine not supported by Aurora&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Development and testing environments&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example: A web application with a backend database that requires SQL Server compatibility and has moderate traffic and performance requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Use Aurora
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Building a new, high-performance application from scratch&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Applications with demanding read-heavy workloads&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Serverless applications with unpredictable traffic patterns&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Global applications that require multi-region database deployments&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example: A large-scale e-commerce platform with millions of daily users, requiring high throughput and low latency for product catalog searches and user profile management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Running Relational Databases on AWS
&lt;/h2&gt;

&lt;p&gt;Regardless of whether you choose RDS or Aurora, here are some best practices to keep in mind when running relational databases on AWS:&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Optimization
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Choose the appropriate instance type and size based on your workload requirements&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Monitor CPU, memory, and I/O utilization to identify bottlenecks and optimize performance&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use caching solutions like ElastiCache to offload read traffic and improve performance&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Security Best Practices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use IAM roles and policies to control access to your database instances&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enable encryption at rest and in transit to protect sensitive data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Regularly apply security patches and updates to your database engine&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use VPC security groups to control network access to your database instances&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Monitoring and Logging
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Enable and configure Amazon CloudWatch for monitoring database metrics and setting alarms&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use AWS CloudTrail to log and audit API activity related to your database instances&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enable database engine-specific logging, such as MySQL slow query logs or PostgreSQL query planner statistics&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scaling and High Availability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use read replicas to scale read traffic and improve performance&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enable Multi-AZ deployments for high availability and automatic failover&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Monitor replication lag and ensure it stays within acceptable limits&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Test failover scenarios regularly to ensure your application can handle database failures gracefully&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;AWS provides two powerful managed services for running them in the cloud: Amazon RDS and Amazon Aurora. RDS is a fully managed service that supports a wide range of database engines, making it a good choice for general-purpose workloads and migrating existing applications to the cloud. Aurora, on the other hand, is a high-performance, MySQL and PostgreSQL-compatible database service that is well-suited for demanding, read-heavy workloads.&lt;/p&gt;

&lt;p&gt;When choosing between RDS and Aurora, it's important to consider your application's specific requirements, including performance, scalability, compatibility, and cost. However, in cases where MySQL or PostgreSQL are suitable, Aurora is generally my preferred choice due to its advanced architecture and auto-scaling capabilities.&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 4000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=hashnode"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real&lt;/strong&gt; scenarios and solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;why&lt;/strong&gt; behind the solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices&lt;/strong&gt; to improve them&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=hashnode"&gt;Subscribe for free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to know more about me, you can find me &lt;a href="https://www.linkedin.com/in/guilleojeda/"&gt;on LinkedIn&lt;/a&gt; or at &lt;a href="https://www.guilleojeda.com?utm_source=blog&amp;amp;utm_medium=hashnode"&gt;www.guilleojeda.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>database</category>
      <category>rds</category>
      <category>aurora</category>
    </item>
    <item>
      <title>Containers on AWS: Comparing ECS and EKS</title>
      <dc:creator>Guille Ojeda</dc:creator>
      <pubDate>Mon, 22 Apr 2024 23:27:27 +0000</pubDate>
      <link>https://forem.com/aws-builders/containers-on-aws-comparing-ecs-and-eks-5h4c</link>
      <guid>https://forem.com/aws-builders/containers-on-aws-comparing-ecs-and-eks-5h4c</guid>
      <description>&lt;p&gt;Containers offer a lightweight, portable, and scalable solution for running software consistently across different environments. But as the number of containers grows, managing them becomes increasingly complex. That's where container orchestration comes in.&lt;/p&gt;

&lt;p&gt;AWS offers two powerful container orchestration services: &lt;strong&gt;Amazon Elastic Container Service (ECS)&lt;/strong&gt; and &lt;strong&gt;Amazon Elastic Kubernetes Service (EKS)&lt;/strong&gt;. Both services help you run and scale containerized applications, but they differ in their approach, features, and use cases.&lt;/p&gt;

&lt;p&gt;In this article, I'll dive deep into the world of containers on AWS. I'll explore the key features and components of ECS and EKS, compare their similarities and differences, and provide guidance on choosing the right service for your needs. By the end, you'll have a solid understanding of how to leverage these services to build and manage containerized applications on AWS effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Amazon ECS (Elastic Container Service)
&lt;/h2&gt;

&lt;p&gt;Let's start by looking at Amazon ECS, AWS's fully managed container orchestration service. ECS allows you to run and manage Docker containers at scale without worrying about the underlying infrastructure.&lt;/p&gt;

&lt;p&gt;ECS Key Features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Fully managed container orchestration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Integration with other AWS services&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Support for both EC2 and Fargate launch types&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Built-in service discovery and load balancing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;IAM integration for security and access control&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ECS Components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Clusters: Logical grouping of container instances or Fargate capacity&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Task Definitions: Blueprints that describe how to run a container&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Services: Maintain a specified number of task replicas and handle scaling&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tasks: Instantiation of a Task Definition, representing a running container&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ECS Launch Types:&lt;/p&gt;

&lt;p&gt;ECS supports two launch types for running containers: EC2 and Fargate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;EC2: You manage the EC2 instances that make up the ECS cluster. This gives you full control over the infrastructure but requires more management overhead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fargate: AWS manages the underlying infrastructure, and you only pay for the resources your containers consume. Fargate abstracts away the EC2 instances, making it easier to focus on your applications.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pricing:&lt;/p&gt;

&lt;p&gt;With ECS, you pay for the AWS resources you use, such as EC2 instances, EBS volumes, and data transfer. Fargate pricing is based on the vCPU and memory resources consumed by your containers.&lt;/p&gt;

&lt;h3&gt;
  
  
  ECS Architecture and Components
&lt;/h3&gt;

&lt;p&gt;Let's take a closer look at the key components of ECS and how they work together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ECS Clusters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An ECS cluster is a logical grouping of container instances or Fargate capacity. It provides the infrastructure to run your containers. You can create clusters using the AWS Management Console, AWS CLI, or CloudFormation templates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Task Definitions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A Task Definition is a JSON file that describes how to run a container. It specifies the container image, CPU and memory requirements, networking settings, and other configuration details. Task Definitions act as blueprints for creating and running tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Services&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An ECS Service maintains a specified number of task replicas and handles scaling. It ensures that the desired number of tasks are running and automatically replaces any failed tasks. Services integrate with ELB for load balancing and service discovery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tasks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A Task is an instantiation of a Task Definition, representing a running container. When you create a task, ECS launches the container on a suitable container instance or Fargate capacity, based on the Task Definition and launch type.&lt;/p&gt;

&lt;p&gt;Example ECS Cluster and Task Definition:&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="nl"&gt;"cluster"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-cluster"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"taskDefinition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-task-definition"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"desiredCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"launchType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FARGATE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"networkConfiguration"&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;"awsvpcConfiguration"&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;"subnets"&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;"subnet-12345678"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"subnet-87654321"&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;"securityGroups"&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;"sg-12345678"&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;"assignPublicIp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ENABLED"&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;h3&gt;
  
  
  ECS Launch Types: EC2 vs Fargate
&lt;/h3&gt;

&lt;p&gt;One key decision when using ECS is choosing between the EC2 and Fargate launch types.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EC2 Launch Type&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With the EC2 launch type, you manage the EC2 instances that make up your ECS cluster. This gives you full control over the infrastructure, including instance types, scaling, and networking. However, it also means more management overhead, as you're responsible for patching, scaling, and securing the instances.&lt;/p&gt;

&lt;p&gt;Use cases for EC2 launch type:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Workloads that require specific instance types or configurations&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Applications that need to access underlying host resources&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scenarios where you want full control over the infrastructure&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fargate Launch Type&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Fargate is a serverless compute engine for containers. It abstracts away the underlying infrastructure, allowing you to focus on your applications. With Fargate, you specify the CPU and memory requirements for your tasks, and ECS manages the rest.&lt;/p&gt;

&lt;p&gt;Benefits of Fargate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No need to manage EC2 instances or clusters&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pay only for the resources your containers consume&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automatic scaling based on task resource requirements&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Simplified infrastructure management&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example of running a containerized application using Fargate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;MyFargateService&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::ECS::Service&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Cluster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;MyCluster&lt;/span&gt;
      &lt;span class="na"&gt;TaskDefinition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;MyTaskDefinition&lt;/span&gt;
      &lt;span class="na"&gt;DesiredCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
      &lt;span class="na"&gt;LaunchType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FARGATE&lt;/span&gt;
      &lt;span class="na"&gt;NetworkConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;AwsvpcConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;AssignPublicIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENABLED&lt;/span&gt;
          &lt;span class="na"&gt;Subnets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;SubnetA&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;SubnetB&lt;/span&gt;
          &lt;span class="na"&gt;SecurityGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;MySecurityGroup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Understanding Amazon EKS (Elastic Kubernetes Service)
&lt;/h2&gt;

&lt;p&gt;Now let's shift gears and explore Amazon EKS, a managed Kubernetes service that makes it easy to deploy, manage, and scale containerized applications using Kubernetes on AWS.&lt;/p&gt;

&lt;p&gt;EKS Key Features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Fully managed Kubernetes control plane&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Integration with AWS services and Kubernetes community tools&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automatic provisioning and scaling of worker nodes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Support for both managed and self-managed node groups&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Built-in security and compliance features&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;EKS Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;EKS consists of two main components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;EKS Control Plane: The control plane is a managed Kubernetes master that runs in an AWS-managed account. It provides the Kubernetes API server, etcd, and other core components.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Worker Nodes: Worker nodes are EC2 instances that run your containers and are registered with the EKS cluster. You can create and manage worker nodes using EKS managed node groups or self-managed worker nodes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pricing:&lt;/p&gt;

&lt;p&gt;With EKS, you pay for the AWS resources you use, such as the EKS control plane, EC2 instances for worker nodes, EBS volumes, and data transfer. You also pay a hourly rate for the EKS control plane based on the number of Kubernetes API requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  EKS Architecture and Components
&lt;/h3&gt;

&lt;p&gt;Let's dive deeper into the EKS architecture and its key components.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EKS Control Plane&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The EKS control plane is a managed Kubernetes master that runs in an AWS-managed account. It provides the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Kubernetes API Server: The primary interface for interacting with the Kubernetes cluster&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;etcd: The distributed key-value store used by Kubernetes to store cluster state&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scheduler: Responsible for scheduling pods onto worker nodes based on resource requirements and constraints&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Controller Manager: Manages the core control loops in Kubernetes, such as replica sets and deployments&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Worker Nodes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Worker nodes are EC2 instances that run your containers and are registered with the EKS cluster. Each worker node runs the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Kubelet: The primary node agent that communicates with the Kubernetes API server and manages container runtime&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Container Runtime: The runtime environment for running containers, such as Docker or containerd&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Kube-proxy: Maintains network rules and performs connection forwarding for Kubernetes services&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example EKS Cluster Configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;eksctl.io/v1alpha5&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterConfig&lt;/span&gt;

&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-eks-cluster&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-west-2&lt;/span&gt;

&lt;span class="na"&gt;managedNodeGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-node-group&lt;/span&gt;
    &lt;span class="na"&gt;instanceType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;t3.medium&lt;/span&gt;
    &lt;span class="na"&gt;minSize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;maxSize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
    &lt;span class="na"&gt;desiredCapacity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  EKS Managed vs Self-Managed Node Groups
&lt;/h3&gt;

&lt;p&gt;EKS provides two options for managing worker nodes: managed node groups and self-managed worker nodes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EKS Managed Node Groups&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;EKS managed node groups automate the provisioning and lifecycle management of worker nodes. Key features include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Automatic provisioning and scaling of worker nodes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Integration with AWS services like VPC and IAM&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Managed updates and patching for worker nodes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Simplified cluster autoscaler configuration&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Self-Managed Worker Nodes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With self-managed worker nodes, you have full control over the provisioning and management of worker nodes. This allows for more customization but also requires more effort to set up and maintain.&lt;/p&gt;

&lt;p&gt;Example of creating an EKS managed node group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eksctl create nodegroup &lt;span class="nt"&gt;--cluster&lt;/span&gt; my-eks-cluster &lt;span class="nt"&gt;--name&lt;/span&gt; my-node-group &lt;span class="nt"&gt;--node-type&lt;/span&gt; t3.medium &lt;span class="nt"&gt;--nodes&lt;/span&gt; 2 &lt;span class="nt"&gt;--nodes-min&lt;/span&gt; 1 &lt;span class="nt"&gt;--nodes-max&lt;/span&gt; 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 4000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  ECS vs EKS: Key Differences and Use Cases
&lt;/h2&gt;

&lt;p&gt;Now that we've explored the key features and components of ECS and EKS, let's compare them side by side.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;ECS&lt;/th&gt;
&lt;th&gt;EKS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Orchestration&lt;/td&gt;
&lt;td&gt;AWS-native orchestration&lt;/td&gt;
&lt;td&gt;Kubernetes orchestration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control Plane&lt;/td&gt;
&lt;td&gt;Fully managed by AWS&lt;/td&gt;
&lt;td&gt;Managed Kubernetes control plane&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure Management&lt;/td&gt;
&lt;td&gt;Managed (Fargate) or self-managed (EC2)&lt;/td&gt;
&lt;td&gt;Managed or self-managed worker nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ecosystem and Tooling&lt;/td&gt;
&lt;td&gt;AWS-native tooling and integrations&lt;/td&gt;
&lt;td&gt;Kubernetes-native tooling and integrations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning Curve&lt;/td&gt;
&lt;td&gt;Simpler, AWS-specific concepts&lt;/td&gt;
&lt;td&gt;Steeper, requires Kubernetes knowledge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Portability&lt;/td&gt;
&lt;td&gt;Tied to AWS ecosystem&lt;/td&gt;
&lt;td&gt;Portable across Kubernetes-compatible platforms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use cases for ECS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Simpler containerized applications&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Workloads that heavily utilize AWS services&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Teams more familiar with AWS ecosystem&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Serverless applications using Fargate&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use cases for EKS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Complex, large-scale containerized applications&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Workloads that require Kubernetes-specific features&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Teams with Kubernetes expertise&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Applications that need to be portable across cloud providers&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Choosing the Right Container Orchestration Service
&lt;/h2&gt;

&lt;p&gt;Choosing between ECS and EKS depends on various factors specific to your application and organizational needs.&lt;/p&gt;

&lt;p&gt;Factors to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Application complexity and scalability&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Team's skills and familiarity with AWS and Kubernetes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Integration with existing tools and workflows&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Long-term container strategy and portability requirements&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to use ECS
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Simpler applications with a limited number of microservices&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Workloads that primarily use AWS services&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Teams more comfortable with AWS tools and concepts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Serverless applications that can benefit from Fargate&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example: A web application consisting of a frontend service, backend API, and database, all running on ECS with Fargate.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to use EKS
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Complex applications with a large number of microservices&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Workloads that require Kubernetes-specific features like Custom Resource Definitions (CRDs)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Teams with extensive Kubernetes experience&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Applications that need to be portable across cloud providers&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example: A large-scale machine learning platform running on EKS, leveraging Kubeflow and other Kubernetes-native tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Container Orchestration on AWS
&lt;/h2&gt;

&lt;p&gt;Regardless of whether you choose ECS or EKS, here are some best practices to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use infrastructure as code (IaC) tools like CloudFormation or Terraform to manage your container orchestration resources&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement a robust CI/CD pipeline to automate container builds, testing, and deployment&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Leverage AWS services like ECR for container image registry and ELB for load balancing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use IAM roles and policies to enforce least privilege access to AWS resources&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Monitor your containerized applications using tools like CloudWatch, Prometheus, or Grafana&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optimize costs by right-sizing your instances, using Spot Instances when appropriate, and leveraging reserved capacity&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;AWS provides two powerful services for container orchestration: ECS and EKS.&lt;/p&gt;

&lt;p&gt;ECS is a fully managed service that offers simplicity and deep integration with the AWS ecosystem. It's well-suited for simpler containerized applications and teams more familiar with AWS tools and concepts.&lt;/p&gt;

&lt;p&gt;On the other hand, EKS is a managed Kubernetes service that provides the full power and flexibility of Kubernetes. It's ideal for complex, large-scale applications and teams with Kubernetes expertise.&lt;/p&gt;

&lt;p&gt;Ultimately, the choice between ECS and EKS depends on your application requirements, team skills, and long-term container strategy. By understanding the key features, differences, and use cases of each service, you can make an informed decision and build scalable, resilient containerized applications on AWS.&lt;/p&gt;

&lt;p&gt;Still, I prefer ECS =)&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 4000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real&lt;/strong&gt; scenarios and solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;why&lt;/strong&gt; behind the solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices&lt;/strong&gt; to improve them&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Subscribe for free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to know more about me, you can find me &lt;a href="https://www.linkedin.com/in/guilleojeda/"&gt;on LinkedIn&lt;/a&gt; or at &lt;a href="https://www.guilleojeda.com"&gt;www.guilleojeda.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>containers</category>
      <category>ecs</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Monitoring and Troubleshooting on AWS: CloudWatch, X-Ray, and Beyond</title>
      <dc:creator>Guille Ojeda</dc:creator>
      <pubDate>Sat, 23 Mar 2024 16:35:19 +0000</pubDate>
      <link>https://forem.com/aws-builders/monitoring-and-troubleshooting-on-aws-cloudwatch-x-ray-and-beyond-3oa3</link>
      <guid>https://forem.com/aws-builders/monitoring-and-troubleshooting-on-aws-cloudwatch-x-ray-and-beyond-3oa3</guid>
      <description>&lt;p&gt;As an AWS user, I'm sure you know that monitoring and troubleshooting are essential for keeping your applications running smoothly. After all, you can't fix what you can't see. But with the sheer number of services and tools available on AWS, it can be overwhelming to know where to start.&lt;/p&gt;

&lt;p&gt;That's where this article comes in. We'll dive into AWS monitoring and troubleshooting, with some key services like CloudWatch and X-Ray, along with other tools and best practices. By the end, you'll have a better understanding of how to effectively monitor and troubleshoot your AWS applications, so you can spend less time fighting fires and more time building cool stuff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding AWS CloudWatch
&lt;/h2&gt;

&lt;p&gt;At the heart of AWS monitoring is CloudWatch, a powerful service that collects monitoring and operational data in the form of logs, metrics, and events. Think of it as the central nervous system of your AWS environment, constantly keeping track of everything that's going on.&lt;/p&gt;

&lt;h3&gt;
  
  
  CloudWatch Metrics
&lt;/h3&gt;

&lt;p&gt;One of the core components of CloudWatch is metrics. CloudWatch Metrics are data points that represent the performance and health of your AWS resources over time. AWS services automatically send metrics to CloudWatch, and you can also publish your own custom metrics.&lt;/p&gt;

&lt;p&gt;For example, EC2 instances automatically send metrics like CPU utilization, network traffic, and disk I/O to CloudWatch. RDS databases send metrics like database connections, read/write latency, and free storage space. By monitoring these metrics, you can get a clear picture of how your resources are performing and identify potential issues before they impact your users.&lt;/p&gt;

&lt;h3&gt;
  
  
  CloudWatch Logs
&lt;/h3&gt;

&lt;p&gt;Another key feature of CloudWatch is logs. CloudWatch Logs allows you to collect, monitor, and store log files from various sources, including EC2 instances, Lambda functions, and on-premises servers. You can use CloudWatch Logs to troubleshoot issues, analyze application behavior, and gain insights into user activity.&lt;/p&gt;

&lt;p&gt;One of the most powerful features of CloudWatch Logs is the ability to filter and search log data. You can use simple text searches or complex query syntax to find specific log events, making it easy to identify errors, exceptions, or other issues. With CloudWatch Logs Insights, you can even perform real-time log analytics, allowing you to quickly investigate and resolve problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  CloudWatch Alarms
&lt;/h3&gt;

&lt;p&gt;Of course, collecting metrics and logs is only half the battle. You also need a way to proactively detect and respond to issues. That's where CloudWatch Alarms come in.&lt;/p&gt;

&lt;p&gt;CloudWatch Alarms allow you to set thresholds for your metrics and receive notifications when those thresholds are breached. For example, you could create an alarm that triggers when the CPU utilization of an EC2 instance exceeds 80% for more than 5 minutes. When the alarm is triggered, you can have CloudWatch send an email, SMS message, or push notification to your team, or even perform automated actions like scaling up your instances or triggering a Lambda function.&lt;/p&gt;

&lt;p&gt;When setting up alarms, it's important to strike a balance between being proactive and being spammed with notifications. A good rule of thumb is to focus on metrics that directly impact the user experience or the stability of your application. You should also carefully consider the thresholds and time periods for your alarms to avoid false positives.&lt;/p&gt;

&lt;h3&gt;
  
  
  CloudWatch Dashboards
&lt;/h3&gt;

&lt;p&gt;Finally, CloudWatch Dashboards provide a way to visualize your metrics and logs in a single, customizable view. Dashboards allow you to create graphs, tables, and other widgets based on your CloudWatch data, giving you a real-time overview of your application's health and performance.&lt;/p&gt;

&lt;p&gt;When creating dashboards, it's important to focus on the metrics and logs that are most relevant to your team and your users. You should also use clear and concise labels and annotations to help your team quickly understand the data being presented. And don't forget to share your dashboards with your team members, so everyone has access to the same information.&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 4000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=hashnode"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  AWS X-Ray: Distributed Tracing for Microservices
&lt;/h2&gt;

&lt;p&gt;While CloudWatch is great for monitoring individual resources and services, it doesn't provide a complete picture of how requests flow through your application. That's where AWS X-Ray comes in.&lt;/p&gt;

&lt;p&gt;X-Ray is a distributed tracing service that allows you to track requests as they move through your application, helping you identify performance bottlenecks, errors, and other issues. X-Ray is especially useful for troubleshooting microservices architectures, where requests often span multiple services and resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Instrumenting Applications for X-Ray
&lt;/h3&gt;

&lt;p&gt;To use X-Ray, you first need to instrument your application code to send tracing data to the X-Ray service. AWS provides X-Ray SDKs for popular programming languages like Java, Node.js, Python, and .NET, which make it easy to add tracing to your code.&lt;/p&gt;

&lt;p&gt;When instrumenting your code, it's important to follow best practices like using meaningful segment names, adding annotations and metadata to your traces, and handling errors gracefully. You should also be careful not to over-instrument your code, as this can add unnecessary overhead and complexity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tracing Requests with X-Ray
&lt;/h3&gt;

&lt;p&gt;Once your application is instrumented, X-Ray will automatically capture and visualize traces as requests flow through your system. The X-Ray service map provides a high-level overview of your application architecture, showing how services and resources are connected and how requests are routed between them.&lt;/p&gt;

&lt;p&gt;By drilling down into individual traces, you can see detailed information about each segment of the request, including response times, errors, and other metadata. This makes it easy to identify performance bottlenecks, such as slow database queries or high network latency, and pinpoint the root cause of issues.&lt;/p&gt;

&lt;p&gt;X-Ray also integrates with other AWS services, allowing you to trace requests as they move between services like API Gateway, Lambda, and DynamoDB. This provides a complete end-to-end view of your application, making it easier to troubleshoot issues that span multiple services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Analyzing and Visualizing Traces
&lt;/h3&gt;

&lt;p&gt;The X-Ray console provides a powerful interface for analyzing and visualizing your tracing data. You can use the console to view the service map, examine individual traces, and filter and group traces based on various attributes like response time, error rate, or user agent.&lt;/p&gt;

&lt;p&gt;One of the most useful features of the X-Ray console is the ability to create custom trace views and dashboards. This allows you to focus on the metrics and traces that are most important to your team, and share those views with other team members.&lt;/p&gt;

&lt;p&gt;You can also integrate X-Ray with CloudWatch, allowing you to create alarms based on X-Ray metrics and visualize X-Ray data alongside other CloudWatch metrics. This provides a more comprehensive view of your application's health and performance, making it easier to identify and resolve issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring Serverless Applications on AWS
&lt;/h2&gt;

&lt;p&gt;Serverless architectures, such as those based on AWS Lambda and Step Functions, present unique challenges when it comes to monitoring and troubleshooting. Because serverless functions are ephemeral and can scale rapidly, traditional monitoring approaches may not be effective.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring AWS Lambda with CloudWatch
&lt;/h3&gt;

&lt;p&gt;One of the key tools for monitoring AWS Lambda is CloudWatch Logs. By default, Lambda sends log output to CloudWatch Logs, allowing you to view and search log data in real-time. You can use CloudWatch Logs to troubleshoot issues, analyze function behavior, and gain insights into performance and usage patterns.&lt;/p&gt;

&lt;p&gt;In addition to logs, Lambda also sends metrics to CloudWatch, including invocations, duration, errors, and throttles. By monitoring these metrics, you can identify performance issues, detect anomalies, and set up alarms to proactively notify you of problems.&lt;/p&gt;

&lt;p&gt;When monitoring Lambda functions, it's important to correlate logs and metrics to get a complete picture of function behavior. For example, if you notice a spike in function duration, you can use CloudWatch Logs to investigate the root cause, such as a slow database query or a network issue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring AWS Step Functions with X-Ray
&lt;/h3&gt;

&lt;p&gt;For more complex serverless workflows, such as those based on AWS Step Functions, X-Ray can be a powerful tool for monitoring and troubleshooting. By enabling X-Ray tracing for your Step Functions, you can visualize the execution flow of your state machines, identify performance bottlenecks, and pinpoint the root cause of errors.&lt;/p&gt;

&lt;p&gt;X-Ray integrates seamlessly with Step Functions, automatically capturing traces as executions move through the state machine. You can use the X-Ray console to view the service map, examine individual executions, and filter and group traces based on various attributes.&lt;/p&gt;

&lt;p&gt;One of the most useful features of X-Ray for Step Functions is the ability to correlate traces across Lambda functions and other AWS services. This allows you to see how data flows through your application, identify performance issues, and troubleshoot errors that span multiple services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other AWS Monitoring and Troubleshooting Tools
&lt;/h2&gt;

&lt;p&gt;While CloudWatch and X-Ray are the core tools for monitoring and troubleshooting on AWS, there are many other services and features that can help you keep your applications running smoothly. Here are a few worth mentioning:&lt;/p&gt;

&lt;h3&gt;
  
  
  Amazon EventBridge
&lt;/h3&gt;

&lt;p&gt;EventBridge is a serverless event bus that makes it easy to build event-driven architectures on AWS. With EventBridge, you can monitor events from a wide range of sources, including AWS services, SaaS applications, and custom applications, and trigger automated actions based on those events.&lt;/p&gt;

&lt;p&gt;For example, you could use EventBridge to monitor EC2 instance state changes, capture S3 bucket events, or detect changes to your AWS resources using CloudTrail. You can then use EventBridge rules to trigger Lambda functions, send SNS notifications, or perform other actions in response to those events.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Config
&lt;/h3&gt;

&lt;p&gt;AWS Config is a service that helps you assess, audit, and evaluate the configurations of your AWS resources. With Config, you can continuously monitor and record resource configurations, and receive notifications when those configurations change.&lt;/p&gt;

&lt;p&gt;Config is particularly useful for troubleshooting issues related to resource misconfigurations or compliance violations. For example, you could use Config to detect when an S3 bucket is made publicly accessible, or when an EC2 instance is launched without the required security group.&lt;/p&gt;

&lt;h3&gt;
  
  
  VPC Flow Logs
&lt;/h3&gt;

&lt;p&gt;VPC Flow Logs is a feature that allows you to capture information about the IP traffic going to and from your VPC. With Flow Logs, you can monitor network traffic at the interface or subnet level, and gain insights into traffic patterns, security issues, and performance bottlenecks.&lt;/p&gt;

&lt;p&gt;Flow Logs can be particularly useful for troubleshooting connectivity issues, detecting unusual traffic patterns, and investigating security incidents. You can use tools like Amazon Athena or Amazon CloudWatch Logs Insights to analyze Flow Log data and identify issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Monitoring and Troubleshooting on AWS
&lt;/h2&gt;

&lt;p&gt;Effective monitoring and troubleshooting on AWS requires more than just the right tools and services. It also requires a well-defined strategy, clear objectives, and a commitment to continuous improvement. Here are some best practices to keep in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Establish clear monitoring and troubleshooting objectives. What are the key metrics and logs that matter most to your application and your users? What are your target response times and error rates? By setting clear objectives upfront, you can focus your monitoring and troubleshooting efforts where they'll have the biggest impact.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a comprehensive monitoring strategy. Your monitoring strategy should cover all aspects of your application, from infrastructure and application metrics to logs and traces. It should also define clear roles and responsibilities for your team, as well as processes for incident response and escalation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement proactive and reactive troubleshooting processes. Proactive troubleshooting involves using monitoring data to identify and resolve issues before they impact users. Reactive troubleshooting involves quickly identifying and resolving issues when they do occur. Both approaches are essential for maintaining a reliable and performant application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Leverage automation and Infrastructure as Code. Automation and Infrastructure as Code (IaC) can help you ensure consistency and reliability across your monitoring and troubleshooting processes. By defining your monitoring configuration as code, you can version control your settings, test changes before applying them, and quickly roll back if needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Continuously optimize your approach. Monitoring and troubleshooting is an ongoing process, not a one-time setup. As your application evolves and your usage patterns change, you'll need to continuously optimize your monitoring and troubleshooting approach to ensure it remains effective. This may involve adding new metrics and logs, adjusting alarm thresholds, or refining your troubleshooting processes.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Monitoring and troubleshooting are essential skills for any AWS user, whether you're running a simple web application or a complex microservices architecture. By using tools like CloudWatch and X-Ray, plus other AWS services and best practices, you can gain deep visibility into your application's behavior and quickly resolve issues when they occur.&lt;/p&gt;

&lt;p&gt;But effective monitoring and troubleshooting is about more than just tools and technology. It's also about having a clear strategy, well-defined processes, and a culture of continuous improvement. By setting clear objectives, implementing proactive and reactive troubleshooting approaches, and continuously optimizing your monitoring and troubleshooting practices, you can build more reliable, performant, and resilient applications on AWS.&lt;/p&gt;

&lt;p&gt;So don't wait until something breaks to start thinking about monitoring and troubleshooting. Start implementing these best practices today, and you'll be well on your way to building better applications on AWS.&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 4000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=hashnode"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real&lt;/strong&gt; scenarios and solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;why&lt;/strong&gt; behind the solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices&lt;/strong&gt; to improve them&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=hashnode"&gt;Subscribe for free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to know more about me, you can find me &lt;a href="https://www.linkedin.com/in/guilleojeda/"&gt;on LinkedIn&lt;/a&gt; or at &lt;a href="https://www.guilleojeda.com"&gt;www.guilleojeda.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>devops</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Security in AWS: IAM Best Practices and Advanced Techniques</title>
      <dc:creator>Guille Ojeda</dc:creator>
      <pubDate>Wed, 20 Mar 2024 00:42:19 +0000</pubDate>
      <link>https://forem.com/aws-builders/security-in-aws-iam-best-practices-and-advanced-techniques-25ac</link>
      <guid>https://forem.com/aws-builders/security-in-aws-iam-best-practices-and-advanced-techniques-25ac</guid>
      <description>&lt;p&gt;AWS IAM (Identity and Access Management) is the backbone of any AWS security strategy. It's the service that controls who can access your AWS resources and what actions they can perform. Get IAM right, and you're well on your way to a secure cloud deployment. Mess it up, and you're leaving the door wide open for all sorts of security nightmares.&lt;/p&gt;

&lt;p&gt;In this article, we'll dive deep into IAM best practices and advanced techniques to help you lock down your AWS environment like a pro. We'll start with the fundamentals, then move on to more advanced topics like granular access control, cross-account access, and automating IAM with Infrastructure as Code. By the end, you'll have a solid understanding of how to use IAM to secure your AWS resources and protect your sensitive data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding IAM Fundamentals
&lt;/h2&gt;

&lt;p&gt;Before we jump into the best practices and advanced techniques, let's make sure we're all on the same page with the IAM basics. Understanding these foundational concepts is crucial for designing and implementing an effective IAM strategy.&lt;/p&gt;

&lt;h3&gt;
  
  
  IAM Users, groups, and roles
&lt;/h3&gt;

&lt;p&gt;At the core of IAM are three main identity types: users, groups, and roles. IAM users represent individual people or applications that need access to your AWS resources. IAM groups are collections of IAM users, making it easier to manage permissions for multiple users at once. IAM roles are a bit different: they're not associated with a specific user, but rather are used by AWS services or external identities that need temporary access to your resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  IAM policies and permissions
&lt;/h3&gt;

&lt;p&gt;IAM policies are JSON documents that define permissions for IAM identities. They specify what actions an identity can perform on which AWS resources. Policies can be attached to IAM users, groups, or roles, or even directly to AWS resources (more on that later).&lt;/p&gt;

&lt;h3&gt;
  
  
  Resource-based policies vs. identity-based policies
&lt;/h3&gt;

&lt;p&gt;There are two main types of IAM policies: identity-based policies and resource-based policies. Identity-based policies are attached to IAM identities (users, groups, or roles) and define what actions those identities can perform on which resources. Resource-based policies, on the other hand, are attached directly to AWS resources (like S3 buckets or KMS keys) and define who can access those resources and what actions they can perform.&lt;/p&gt;

&lt;h3&gt;
  
  
  How IAM interacts with other AWS services
&lt;/h3&gt;

&lt;p&gt;IAM is deeply integrated with other AWS services. It's used to control access to virtually every AWS resource, from EC2 instances to S3 buckets to Lambda functions. Many AWS services also have their own resource-based policies that work in conjunction with IAM policies to provide fine-grained access control.&lt;/p&gt;

&lt;h2&gt;
  
  
  IAM Best Practices
&lt;/h2&gt;

&lt;p&gt;Now that we've got the fundamentals down, let's dive into some IAM best practices that every AWS user should follow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Principle of least privilege
&lt;/h3&gt;

&lt;p&gt;The principle of least privilege is the golden rule of IAM. It means only granting users the permissions they need to perform their job duties; no more, no less. This helps minimize the blast radius if a user's credentials are compromised, and makes it easier to audit and manage permissions over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Proper IAM user and role management
&lt;/h3&gt;

&lt;p&gt;Managing IAM users and roles can get complex, especially in large organizations. Some key best practices include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create individual IAM users for each person who needs access to AWS, rather than sharing credentials&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use IAM roles for applications and services that need access to AWS resources&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Regularly review and remove unused IAM users and roles&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Using IAM groups for better organization
&lt;/h3&gt;

&lt;p&gt;IAM groups make it easier to manage permissions for multiple users at once. By creating groups for different job functions or teams, you can assign permissions at the group level rather than individually. This makes it easier to onboard new users and ensure consistent permissions across your organization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Password policies and MFA enforcement
&lt;/h3&gt;

&lt;p&gt;Strong password policies and multi-factor authentication (MFA) are critical for protecting your IAM users. AWS allows you to set password policies that enforce minimum length, complexity, and rotation requirements. You should also require MFA for all IAM users, especially those with administrative privileges.&lt;/p&gt;

&lt;h3&gt;
  
  
  Regularly reviewing and rotating IAM credentials
&lt;/h3&gt;

&lt;p&gt;Over time, IAM users can accumulate unnecessary permissions, and credentials can become stale or compromised. That's why it's important to regularly review IAM users and their permissions, and rotate access keys and passwords on a regular basis. AWS recommends rotating access keys every 90 days, and immediately revoking credentials for users who leave your organization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoiding use of root user account
&lt;/h3&gt;

&lt;p&gt;The root user account has unrestricted access to all AWS resources in your account, making it a prime target for attackers. Best practice is to avoid using the root user account for day-to-day tasks, and instead create individual IAM users with specific permissions. You should also enable MFA on the root user account and use it only for tasks that absolutely require root privileges.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing Granular Access Control
&lt;/h3&gt;

&lt;p&gt;One of the most powerful features of IAM is the ability to create fine-grained policies that precisely control access to your AWS resources. Here are some techniques for implementing granular access control:&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating fine-grained IAM policies
&lt;/h3&gt;

&lt;p&gt;When creating IAM policies, it's important to be as specific as possible. Instead of granting broad permissions like &lt;code&gt;s3:*&lt;/code&gt;, grant only the specific actions needed, like &lt;code&gt;s3:GetObject&lt;/code&gt; or &lt;code&gt;s3:PutObject&lt;/code&gt;. You can also restrict access to specific resources using ARNs (Amazon Resource Names), and limit permissions to specific IP ranges or VPC endpoints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using policy conditions for more precise control
&lt;/h3&gt;

&lt;p&gt;IAM policy conditions allow you to further refine permissions based on specific criteria. For example, you can use conditions to allow access only during certain time windows, from specific IP ranges, or for requests that include certain headers or parameters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Leveraging IAM policy variables
&lt;/h3&gt;

&lt;p&gt;IAM policy variables allow you to create dynamic policies that adapt to your environment. For example, you can use the &lt;code&gt;aws:username&lt;/code&gt; variable to grant users access to their own home directory in an S3 bucket, or the &lt;code&gt;aws:SourceIp&lt;/code&gt; variable to restrict access based on the requester's IP address.&lt;/p&gt;

&lt;h3&gt;
  
  
  Combining multiple policies for complex permissions
&lt;/h3&gt;

&lt;p&gt;In some cases, you may need to combine multiple policies to achieve the desired level of access control. For example, you might use an identity-based policy to grant broad permissions to a group of users, then use a resource-based policy to further restrict access to specific resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world examples of granular access control in AWS
&lt;/h2&gt;

&lt;p&gt;Let's look at a couple real-world examples of granular access control in action:&lt;/p&gt;

&lt;h3&gt;
  
  
  Granting read-only access to an S3 bucket for a specific IAM user
&lt;/h3&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="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&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;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ReadOnlyAccess"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&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;"s3:GetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"s3:ListBucket"&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;"Resource"&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;"arn:aws:s3:::my-bucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::my-bucket/*"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Allowing an EC2 instance to access S3, but only from a specific VPC endpoint
&lt;/h3&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="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&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;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AccessFromVPCEndpoint"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"Condition"&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;"StringEquals"&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;"aws:sourceVpce"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vpce-1a2b3c4d"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cross-Account Access and IAM Roles
&lt;/h2&gt;

&lt;p&gt;In many organizations, you'll need to grant access to AWS resources across multiple accounts. That's where IAM roles and cross-account access come in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding cross-account access
&lt;/h3&gt;

&lt;p&gt;Cross-account access allows IAM users or roles in one AWS account to access resources in another account. This is useful for scenarios like granting developers access to a production account, or allowing a central security team to monitor multiple accounts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using IAM roles for secure access delegation
&lt;/h3&gt;

&lt;p&gt;IAM roles are the preferred way to grant cross-account access. Instead of sharing access keys or passwords, you create an IAM role in the target account and grant permissions to the trusted entity (user or role) in the source account. The trusted entity can then assume the role and access resources in the target account.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assuming roles vs. using access keys
&lt;/h3&gt;

&lt;p&gt;When accessing resources across accounts, it's best to assume an IAM role rather than using access keys. Access keys are long-term credentials that can be easily leaked or compromised, while IAM roles provide temporary, short-lived credentials that automatically expire.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best practices for managing cross-account access
&lt;/h3&gt;

&lt;p&gt;Some best practices for managing cross-account access include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use IAM roles for cross-account access instead of sharing long-term access keys&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Limit the permissions granted to cross-account roles to the minimum necessary&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Regularly review and audit cross-account access&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use external ID's to prevent the confused deputy problem&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Securing Access to AWS Resources
&lt;/h2&gt;

&lt;p&gt;In addition to identity-based policies, AWS also supports resource-based policies that allow you to control access to specific resources like S3 buckets, KMS keys, and Lambda functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using resource-based policies (e.g., S3 bucket policies)
&lt;/h3&gt;

&lt;p&gt;Resource-based policies are attached directly to an AWS resource and define who can access that resource and what actions they can perform. For example, an S3 bucket policy can allow read access to objects from a specific IP range, or deny all public access to the bucket.&lt;/p&gt;

&lt;h3&gt;
  
  
  Combining resource-based and identity-based policies
&lt;/h3&gt;

&lt;p&gt;Resource-based policies work in conjunction with identity-based policies to provide comprehensive access control. When an IAM user or role tries to access a resource, AWS evaluates both the identity-based policies attached to the user/role and the resource-based policy attached to the resource. Access is granted only if both policies allow it.&lt;/p&gt;

&lt;h3&gt;
  
  
  VPC endpoints and IAM policies
&lt;/h3&gt;

&lt;p&gt;VPC endpoints allow you to securely access AWS services from within your VPC, without traversing the public internet. You can use IAM policies to control access to VPC endpoints, ensuring that only authorized users or roles can access the services behind the endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  Securing access to API Gateway and Lambda
&lt;/h3&gt;

&lt;p&gt;API Gateway and Lambda are powerful tools for building serverless applications, but they also introduce new security challenges. Best practices for securing access to these services include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use IAM roles to grant Lambda functions access to other AWS services&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement OAuth or JWT authentication for APIs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use API keys and usage plans to control access to APIs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enable AWS WAF to protect against common web exploits&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Protecting sensitive data with KMS and IAM
&lt;/h3&gt;

&lt;p&gt;AWS Key Management Service (KMS) allows you to encrypt your sensitive data using centrally managed keys. IAM policies can be used to control access to KMS keys, ensuring that only authorized users or roles can encrypt or decrypt data.&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 4000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Centralized IAM Management with AWS Organizations
&lt;/h2&gt;

&lt;p&gt;For organizations with multiple AWS accounts, managing IAM across all those accounts can be a challenge. That's where AWS Organizations comes in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits of using AWS Organizations
&lt;/h3&gt;

&lt;p&gt;AWS Organizations allows you to centrally manage access across multiple accounts. You can create an organization, invite accounts to join, and then use Service Control Policies (SCPs) to enforce IAM policies across all accounts in the organization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up an organization and creating member accounts
&lt;/h3&gt;

&lt;p&gt;To get started with AWS Organizations, you create an organization and invite existing accounts to join, or create new accounts directly within the organization. You can organize accounts into Organizational Units (OUs) to apply policies hierarchically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing Service Control Policies (SCPs)
&lt;/h3&gt;

&lt;p&gt;Service Control Policies are a powerful feature of AWS Organizations that allow you to centrally control what actions can be performed by IAM users and roles across all accounts in your organization. SCPs are similar to IAM policies, but they apply at the account level and can be used to enforce security best practices and compliance requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delegating access across accounts with IAM roles
&lt;/h3&gt;

&lt;p&gt;In addition to SCPs, AWS Organizations also simplifies cross-account access using IAM roles. You can create a role in a central account and grant access to users or roles in other accounts within the organization. This allows you to centrally manage permissions while still enabling teams to access the resources they need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best practices for AWS Organizations
&lt;/h3&gt;

&lt;p&gt;Some best practices for using AWS Organizations include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use SCPs to enforce security best practices and compliance requirements&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement a least privilege model, granting only the permissions necessary for each account&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use AWS CloudTrail to monitor IAM activity across all accounts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Regularly review and audit IAM policies and roles&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use automation tools like AWS CloudFormation to manage IAM resources consistently across accounts&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Monitoring and Auditing IAM Activity with AWS CloudTrail
&lt;/h2&gt;

&lt;p&gt;Monitoring and auditing IAM activity is critical for detecting and responding to security incidents. AWS CloudTrail is a powerful tool for tracking IAM activity across your AWS accounts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Importance of monitoring IAM events
&lt;/h3&gt;

&lt;p&gt;By monitoring IAM events, you can detect suspicious activity like unauthorized access attempts, changes to IAM policies, or creation of new IAM users or roles. This allows you to quickly investigate and respond to potential security breaches.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using AWS CloudTrail to track IAM actions
&lt;/h3&gt;

&lt;p&gt;AWS CloudTrail logs all API calls made to IAM, including who made the call, what actions were performed, and what resources were affected. You can use CloudTrail to create a complete audit trail of IAM activity in your account.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring IAM events with Amazon CloudWatch
&lt;/h3&gt;

&lt;p&gt;In addition to CloudTrail, you can use Amazon CloudWatch to monitor IAM events in real-time. CloudWatch allows you to create alarms based on specific IAM events, like failed login attempts or changes to sensitive policies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Detecting and alerting on suspicious IAM activity
&lt;/h3&gt;

&lt;p&gt;By combining CloudTrail and CloudWatch, you can create a comprehensive monitoring and alerting system for IAM. Some best practices include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create alarms for high-risk events like IAM policy changes or root account usage&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use CloudTrail Insights to detect unusual activity patterns&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Integrate with SIEM tools like Splunk or AWS Security Hub for centralized monitoring&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conducting regular IAM audits and compliance checks
&lt;/h3&gt;

&lt;p&gt;In addition to real-time monitoring, it's important to conduct regular IAM audits to ensure your policies and permissions are configured correctly and comply with your security and compliance requirements. Tools like AWS IAM Access Analyzer and AWS Config can help automate this process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced IAM Security Features
&lt;/h2&gt;

&lt;p&gt;These are some more advanced features of AWS IAM, or some related services that will help you secure your AWS accounts and workloads.&lt;/p&gt;

&lt;h3&gt;
  
  
  IAM Access Analyzer
&lt;/h3&gt;

&lt;p&gt;AWS IAM Access Analyzer is a powerful tool for identifying unintended access to your AWS resources. It analyzes your IAM policies and resource-based policies to determine who has access to your resources and whether that access is intended.&lt;/p&gt;

&lt;p&gt;IAM Access Analyzer can help you identify scenarios like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Public access to S3 buckets or other resources&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Access granted to external AWS accounts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Overly permissive IAM policies&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By identifying these issues early, you can take corrective action before they lead to a security breach.&lt;/p&gt;

&lt;h3&gt;
  
  
  IAM Permission Boundaries
&lt;/h3&gt;

&lt;p&gt;IAM Permission Boundaries are a way to limit the maximum permissions that can be granted to an IAM user or role. They're useful for scenarios like allowing developers to create their own IAM policies, but ensuring they can't grant themselves excessive permissions.&lt;/p&gt;

&lt;p&gt;To implement a permission boundary, you create an IAM policy that defines the maximum permissions allowed, then attach that policy as a permission boundary to an IAM user or role. Any policies attached to the user or role are evaluated within the constraints of the permission boundary.&lt;/p&gt;

&lt;h3&gt;
  
  
  IAM Policy Conditions
&lt;/h3&gt;

&lt;p&gt;IAM Policy Conditions allow you to create more fine-grained access control policies based on specific attributes of a request, like the source IP address, time of day, or presence of multi-factor authentication.&lt;/p&gt;

&lt;p&gt;Some examples of using IAM policy conditions include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Allowing access only during business hours&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Requiring multi-factor authentication for sensitive actions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Restricting access to specific IP ranges or VPC endpoints&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  IAM Identity Center for AWS SSO
&lt;/h3&gt;

&lt;p&gt;IAM Identity Center (formerly AWS Single Sign-On) is a centralized access management service that allows users to sign in once and access multiple AWS accounts and cloud applications.&lt;/p&gt;

&lt;p&gt;With IAM Identity Center, you can create and manage user identities in a central directory, then assign permissions to those users across multiple AWS accounts. Users sign in once to the IAM Identity Center portal, then access their assigned accounts and applications without needing to manage separate credentials.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integrating IAM Identity Center with third-party identity providers
&lt;/h3&gt;

&lt;p&gt;IAM Identity Center also allows you to integrate with third-party identity providers like Azure AD, Okta, or Ping Identity. This allows you to use your existing identity management system to control access to AWS, without needing to recreate user identities in IAM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating IAM with Infrastructure as Code Tools
&lt;/h2&gt;

&lt;p&gt;As your AWS environment grows, managing IAM policies and roles manually becomes increasingly difficult. That's where Infrastructure as Code (IaC) tools like AWS CloudFormation, Terraform, and the AWS CDK come in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits of using Infrastructure as Code (IaC) for IAM
&lt;/h3&gt;

&lt;p&gt;By defining your IAM resources as code, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Version control your IAM policies and roles&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automate the creation and updates of IAM resources&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensure consistency across multiple AWS accounts and regions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easily roll back changes if needed&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Using AWS CloudFormation to manage IAM resources
&lt;/h3&gt;

&lt;p&gt;AWS CloudFormation is a native AWS service that allows you to define your infrastructure as code using JSON or YAML templates. You can use CloudFormation to create and manage IAM users, groups, roles, and policies across multiple accounts and regions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Terraform and AWS CDK for IAM automation
&lt;/h3&gt;

&lt;p&gt;Terraform and the AWS Cloud Development Kit (CDK) are popular third-party IaC tools that support IAM resource management. Terraform uses a declarative language called HCL (HashiCorp Configuration Language) to define infrastructure resources, while the AWS CDK allows you to define infrastructure using familiar programming languages like JavaScript, TypeScript, Python, or Java.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best practices for IAM automation and version control
&lt;/h3&gt;

&lt;p&gt;When automating IAM with IaC tools, it's important to follow best practices like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Storing your IaC templates in a version control system like Git&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using separate AWS accounts for development, staging, and production environments&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implementing a code review process for IAM changes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using tools like AWS CloudTrail and AWS Config to monitor and audit IAM changes&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By treating your IAM resources as code and following these best practices, you can ensure consistency, maintainability, and auditability of your IAM configuration.&lt;/p&gt;

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

&lt;p&gt;IAM is a critical component of securing your AWS environment, but it can be really complex and challenging to manage at scale. By following best practices like the &lt;strong&gt;principle of least privilege&lt;/strong&gt;, &lt;strong&gt;using IAM roles for cross-account access&lt;/strong&gt;, and implementing &lt;strong&gt;strong password policies and MFA&lt;/strong&gt;, you can lay a solid foundation for your IAM strategy.&lt;/p&gt;

&lt;p&gt;But to truly secure your accounts and environments, you need to go beyond the basics. Techniques like &lt;strong&gt;granular access control with policy conditions&lt;/strong&gt;, &lt;strong&gt;resource-based policies&lt;/strong&gt;, and &lt;strong&gt;permission boundaries&lt;/strong&gt; allow you to implement fine-grained security policies that precisely control access to your resources. &lt;strong&gt;Centralized management with AWS Organizations&lt;/strong&gt; and &lt;strong&gt;monitoring with CloudTrail and CloudWatch&lt;/strong&gt; provide visibility and actionable data across your entire AWS environment.&lt;/p&gt;

&lt;p&gt;As your AWS usage grows, &lt;strong&gt;automating IAM with Infrastructure as Code&lt;/strong&gt; tools like CloudFormation, Terraform, and the AWS CDK becomes increasingly important. By defining your IAM resources as code and following best practices for version control and testing, you can ensure consistency and maintainability of your IAM configuration.&lt;/p&gt;

&lt;p&gt;Securing your AWS environment is an ongoing process, not a one-time task. As you adopt new AWS services and your application requirements evolve, it's important to continually review and update your IAM policies to ensure they align with your security goals. Regular audits and compliance checks, along with automated monitoring and alerting, can help you stay on top of your IAM configuration and quickly detect and respond to potential issues.&lt;/p&gt;

&lt;p&gt;By following the best practices and techniques outlined in this article, you can build a robust and secure IAM strategy that helps you protect your critical AWS resources and data. But don't stop here! Continue to explore and adopt new security services and features like AWS GuardDuty, AWS Security Hub, and AWS Secrets Manager to further strengthen your security posture.&lt;/p&gt;

&lt;p&gt;Remember, security is a shared responsibility between AWS and you, the customer. By taking a proactive and layered approach to IAM and security, you can ensure that your AWS environment is protected against evolving threats and ready to support your business needs for years to come.&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 4000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real&lt;/strong&gt; scenarios and solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;why&lt;/strong&gt; behind the solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices&lt;/strong&gt; to improve them&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Subscribe for free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to know more about me, you can find me &lt;a href="https://www.linkedin.com/in/guilleojeda/"&gt;on LinkedIn&lt;/a&gt; or at &lt;a href="https://www.guilleojeda.com?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;www.guilleojeda.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>security</category>
    </item>
    <item>
      <title>Disaster Recovery Strategies on AWS: Ensuring Business Continuity</title>
      <dc:creator>Guille Ojeda</dc:creator>
      <pubDate>Fri, 15 Mar 2024 01:08:06 +0000</pubDate>
      <link>https://forem.com/aws-builders/disaster-recovery-strategies-on-aws-ensuring-business-continuity-1lgh</link>
      <guid>https://forem.com/aws-builders/disaster-recovery-strategies-on-aws-ensuring-business-continuity-1lgh</guid>
      <description>&lt;p&gt;We're now living in the world of immediate and always-on stuff, where even a few minutes of downtime can be a disaster for businesses. Customers expect 24/7 availability, and any interruption in service can lead to lost revenue, damaged reputation, and even legal consequences. That's where disaster recovery (DR) and business continuity planning come into play.&lt;/p&gt;

&lt;p&gt;Disaster recovery is all about preparing for the worst-case scenarios—those unexpected events that can bring your systems to a halt. Whether it's a natural disaster, human error, or a cyber-attack (which is often also caused by human error), having a solid DR plan in place can make the difference between a minor hiccup and a catastrophic failure.&lt;/p&gt;

&lt;p&gt;Amazon Web Services (AWS) offers a wide variety of services and features to help you build robust, resilient architectures that can continue operating in the event of a disaster. In this article, we'll explore the key concepts and strategies for implementing effective disaster recovery on AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding RTO and RPO
&lt;/h2&gt;

&lt;p&gt;Before we dive into specific DR strategies, let's take a moment to define two critical metrics: &lt;strong&gt;Recovery Time Objective (RTO)&lt;/strong&gt; and &lt;strong&gt;Recovery Point Objective (RPO)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;RTO is the maximum acceptable amount of time your systems can be down when a disaster occurs. In other words, it's the timeframe within which you need to restore your applications and data to avoid unacceptable consequences. For example, a financial trading platform might have an RTO of just a few minutes, while a less critical internal tool might have an RTO of several hours.&lt;/p&gt;

&lt;p&gt;RPO, on the other hand, refers to the maximum acceptable amount of data loss your business can tolerate. It's determined by how frequently you take backups and how much data you're willing to lose in the event of a disaster. For instance, an e-commerce site might have an RPO of just a few seconds, meaning they can only afford to lose a very small amount of data, while a blog might be okay with losing a day's worth of content.&lt;/p&gt;

&lt;p&gt;Your RTO and RPO will heavily influence your choice of DR strategies. The tighter your objectives, the more robust (and expensive) your DR solution will need to be. It's all about finding the right balance between cost and risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing a Highly Available Architecture on AWS
&lt;/h2&gt;

&lt;p&gt;The first step before even thinking about disaster recovery is a highly available architecture. On AWS, that means leveraging multiple Availability Zones (AZs) to build redundancy and fault tolerance into your applications.&lt;/p&gt;

&lt;p&gt;AWS operates a global network of data centers, grouped into regions and further subdivided into AZs. Each AZ is a fully isolated partition of the AWS infrastructure, with independent power, cooling, and networking. By deploying your applications across multiple AZs within a region, you can protect against failures at the data center level.&lt;/p&gt;

&lt;p&gt;Of course, building a highly available architecture involves more than just spreading your resources across AZs. You'll also need to implement load balancing and auto-scaling to distribute traffic evenly and automatically adjust capacity based on demand. Services like Amazon EC2 Auto Scaling and Elastic Load Balancing make this easy to achieve.&lt;/p&gt;

&lt;p&gt;But what if an entire region goes down? That's where multi-region architectures come into play. By replicating your data and applications across multiple AWS regions, you can ensure that even if an entire region becomes unavailable, your business can continue to operate from another location.&lt;/p&gt;

&lt;p&gt;That is what we call Disaster Recovery.&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 4000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Disaster Recovery Strategies on AWS
&lt;/h2&gt;

&lt;p&gt;Now that we've covered the basics of high availability, let's explore &lt;a href="https://newsletter.simpleaws.dev/p/disaster-recovery-strategies-aws?utm_source=blog?utm_medium=dev.to"&gt;four common DR strategies&lt;/a&gt; you can implement on AWS: backup and restore, pilot light, warm standby, and multi-site active-active.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backup and Restore Strategy
&lt;/h3&gt;

&lt;p&gt;The backup and restore strategy is the most basic and cost-effective approach to DR on AWS. It involves taking regular backups of your data and storing them in a secure, durable location like Amazon S3. In the event of a disaster, you can restore your systems from the most recent backup. While simple, this strategy typically involves significant downtime, as you'll need to provision new infrastructure and restore your data before your applications can be brought back online. It's best suited for non-critical workloads with lenient RTO and RPO requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pilot Light Strategy
&lt;/h3&gt;

&lt;p&gt;The pilot light strategy involves keeping a minimal version of your environment running in a secondary region, ready to scale up quickly in the event of a disaster. Core components, like your database servers, are always on, but application servers are kept in a stopped state to minimize costs. When disaster strikes, you can quickly start up your application servers, scale them out to handle the full production load, and redirect traffic to the secondary region. This approach offers faster recovery times than the backup and restore strategy, but still involves some downtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  Warm Standby Strategy
&lt;/h3&gt;

&lt;p&gt;The warm standby strategy takes the pilot light approach a step further. Instead of keeping your secondary environment in a minimal state, you maintain a scaled-down version of your full production environment in the secondary region, with all components running. In the event of a disaster, you can rapidly scale up the secondary environment to handle the full production load. This strategy provides even faster recovery times than the pilot light approach, but comes with higher ongoing costs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-Site Active-Active Strategy
&lt;/h3&gt;

&lt;p&gt;The multi-site active-active strategy is the most comprehensive and expensive DR approach. It involves running your full production environment in multiple regions simultaneously, with each region serving traffic and replicating data in real-time. If one region fails, traffic is automatically routed to the other active region(s) without any interruption in service. This strategy provides the highest level of availability and the fastest recovery times, but also incurs the highest costs, as you're essentially running multiple copies of your entire infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Create Backups on AWS
&lt;/h2&gt;

&lt;p&gt;Regardless of which DR strategy you choose, creating regular backups is a critical component of any DR plan. AWS offers several backup services and features to help you protect your data.&lt;/p&gt;

&lt;p&gt;For Amazon EC2 instances, you can create point-in-time snapshots of your EBS volumes, which can be used to restore your instances to a previous state. You can automate the creation and management of EBS snapshots using AWS Backup, a fully managed backup service that simplifies the process of backing up your AWS resources.&lt;/p&gt;

&lt;p&gt;For managed database services like Amazon RDS and Amazon DynamoDB, automated backups are typically enabled by default. You can also create manual snapshots for longer-term retention or to copy your backups to another region for DR purposes.&lt;/p&gt;

&lt;p&gt;It's important to regularly test your backups to ensure they can be successfully restored in the event of a disaster. You should also consider implementing a backup retention policy to ensure you have the right balance of short-term and long-term backups to meet your RPO requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replication and Failover Strategies
&lt;/h2&gt;

&lt;p&gt;In addition to backups, replication and failover are key components of many DR strategies on AWS. By replicating your data and applications across multiple regions, you can ensure that even if an entire region becomes unavailable, your business can continue to operate from another location.&lt;/p&gt;

&lt;p&gt;AWS offers several services and features to help you implement cross-region replication and failover. For example, you can use Amazon S3 Cross-Region Replication to automatically replicate objects across S3 buckets in different AWS regions. For databases, you can use Amazon RDS Read Replicas or Amazon Aurora Global Database to create cross-region read replicas that can be quickly promoted to standalone instances in the event of a disaster.&lt;/p&gt;

&lt;p&gt;When it comes to failover, you'll need to consider both application-level and DNS-level strategies. At the application level, you can use services like Amazon Route 53 Application Recovery Controller to continuously monitor your application's health and automatically route traffic to healthy resources in the event of a failure.&lt;/p&gt;

&lt;p&gt;For DNS failover, Amazon Route 53 offers a variety of routing policies that can help you direct traffic to the appropriate region based on factors like latency, geography, and resource health. By combining these strategies, you can create a robust, automated failover solution that minimizes downtime and ensures your applications remain available even in the face of a regional outage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disaster Recovery Automation and Testing
&lt;/h2&gt;

&lt;p&gt;Automation is key to implementing an effective DR strategy on AWS. By declaring your infrastructure as code using tools like AWS CloudFormation and Terraform, you can ensure that your DR environment can be quickly and consistently provisioned in the event of a disaster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure as Code (IaC)&lt;/strong&gt; not only speeds up the recovery process, but also reduces the risk of human error and ensures that your DR environment is always in a known, consistent state. You can use IaC templates to define everything from your network topology to your application configurations, making it easy to spin up an exact replica of your production environment in a secondary region.&lt;/p&gt;

&lt;p&gt;Regular testing is also essential to ensuring the viability of your DR plan. You should schedule periodic DR drills to simulate different failure scenarios and validate that your recovery processes work as expected. These drills can help you identify gaps in your plan and areas for improvement, ensuring that you're always prepared for a real-world disaster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chaos Engineering on AWS
&lt;/h2&gt;

&lt;p&gt;In addition to traditional DR testing, you may also want to consider implementing chaos engineering practices to proactively identify weaknesses in your systems. Chaos engineering involves intentionally injecting failures into your environment to test its resilience and uncover hidden vulnerabilities.&lt;/p&gt;

&lt;p&gt;AWS offers a service called AWS Fault Injection Simulator (FIS) that makes it easy to perform controlled chaos experiments on your AWS workloads. With FIS, you can simulate a variety of failure scenarios, like EC2 instance terminations, API throttling, and network latency, and observe how your applications respond.&lt;/p&gt;

&lt;p&gt;By regularly performing chaos experiments, you can build confidence in your systems' ability to withstand failures and identify opportunities for improvement before a real disaster strikes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring and Alerting for Disaster Recovery
&lt;/h2&gt;

&lt;p&gt;Effective monitoring and alerting are critical components of any DR strategy. You need to be able to quickly detect and respond to issues before they escalate into full-blown disasters.&lt;/p&gt;

&lt;p&gt;AWS offers a range of monitoring and logging services, like Amazon CloudWatch and AWS X-Ray, that can help you gain visibility into the health and performance of your applications. CloudWatch allows you to collect and track metrics, collect and monitor log files, and set alarms that notify you when thresholds are breached. X-Ray helps you analyze and debug distributed applications, providing insights into how your services are interacting and performing.&lt;/p&gt;

&lt;p&gt;In addition to these services, you should also consider implementing a robust alerting strategy using Amazon Simple Notification Service (SNS). With SNS, you can send notifications via email, SMS, or even trigger automated remediation actions when specific events occur or thresholds are crossed.&lt;/p&gt;

&lt;p&gt;By combining comprehensive monitoring with proactive alerting, you can ensure that you're always aware of the state of your environment and can quickly respond to any issues that arise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost Optimization for Disaster Recovery
&lt;/h2&gt;

&lt;p&gt;Implementing a comprehensive DR strategy can be expensive, especially if you're maintaining a fully replicated environment in a secondary region. However, there are several strategies you can use to optimize your costs without compromising on your DR objectives.&lt;/p&gt;

&lt;p&gt;One approach is to leverage AWS cost-saving features like Reserved Instances and Spot Instances for your DR environment. By purchasing Reserved Instances, you can significantly reduce your EC2 costs compared to On-Demand pricing. Spot Instances allow you to bid on spare EC2 capacity at steep discounts, which can be ideal for non-critical DR workloads.&lt;/p&gt;

&lt;p&gt;Another strategy is to tiered approach to DR, using different strategies for different parts of your application stack based on their criticality and recovery requirements. For example, you might use a multi-site active-active approach for your most critical databases, but a pilot light approach for less critical application tiers.&lt;/p&gt;

&lt;p&gt;Continuously monitoring and optimizing your DR costs is also important. You should regularly review your DR environment to identify any underutilized or unnecessary resources, and adjust your strategy accordingly. Tools like AWS Cost Explorer and AWS Budgets can help you track your spending and set alerts when you're approaching your budget limits.&lt;/p&gt;

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

&lt;p&gt;Implementing an effective disaster recovery strategy on AWS requires careful planning, robust architecture, and regular testing and optimization. By leveraging the right mix of AWS services and features, you can create a DR solution that meets your business's unique requirements for availability, recovery time, and data protection.&lt;/p&gt;

&lt;p&gt;To recap, the four main DR strategies you can implement on AWS are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Backup and restore:&lt;/strong&gt; Periodically backing up your data and resources, and restoring them in the event of a disaster.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pilot light:&lt;/strong&gt; Maintaining a minimal version of your environment in a secondary region, ready to scale up when needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Warm standby:&lt;/strong&gt; Running a scaled-down version of your full environment in a secondary region, with the ability to quickly scale up to handle the full production load.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multi-site active-active:&lt;/strong&gt; Running your full production environment simultaneously in multiple regions, with automatic failover between regions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Regardless of which strategy you choose, it's critical to regularly test and refine your DR plan to ensure it remains effective as your business evolves. By combining comprehensive monitoring, automated failover, and regular chaos engineering practices, you can build a resilient, highly available application that can weather any storm.&lt;/p&gt;

&lt;p&gt;Remember, disaster recovery planning isn't a one-time exercise—it's an ongoing process that requires continuous improvement and optimization. By staying proactive and prepared, you can ensure that your business can continue to operate and thrive, no matter what challenges come your way.&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 4000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real&lt;/strong&gt; scenarios and solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;why&lt;/strong&gt; behind the solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices&lt;/strong&gt; to improve them&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Subscribe for free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to know more about me, you can find me &lt;a href="https://www.linkedin.com/in/guilleojeda/"&gt;on LinkedIn&lt;/a&gt; or at &lt;a href="https://www.guilleojeda.com?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;www.guilleojeda.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Understanding Amazon S3 Pricing</title>
      <dc:creator>Guille Ojeda</dc:creator>
      <pubDate>Thu, 01 Feb 2024 15:13:17 +0000</pubDate>
      <link>https://forem.com/aws-builders/understanding-amazon-s3-pricing-4j4c</link>
      <guid>https://forem.com/aws-builders/understanding-amazon-s3-pricing-4j4c</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;What is Amazon S3&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Amazon Simple Storage Service (S3) is an object storage service by AWS that can store any kind of information. S3 is known for its durability, availability, and scalability, and the fact that all of these features come out of the box makes S3 a go-to solution for a wide range of data storage needs.&lt;/p&gt;

&lt;p&gt;In S3 users create 'buckets' – containers for data stored in the AWS cloud. Storing data in buckets serves various use cases, from website hosting to backup and recovery, data archiving, and big data analytics.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;S3 Storage Classes&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Amazon S3 offers several storage classes designed for different use cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;S3 Standard&lt;/strong&gt;: For frequently accessed data. You're billed per storage and per request.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;S3 Standard-IA (Infrequent Access)&lt;/strong&gt;: For data that is accessed less frequently but requires rapid access when needed. Lower fee per GB stored than Standard, but a higher fee per request.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;S3 One Zone-IA&lt;/strong&gt;: Similar to Standard-IA, but data is stored in a single Availability Zone only, and it's also cheaper.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;S3 Express One Zone&lt;/strong&gt;: High-performance storage for your most frequently accessed data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;S3 Intelligent-Tiering&lt;/strong&gt;: Automatically moves data between the Standard and Standard-IA tiers based on continuously evaluating your access patterns. Ideal for data with unknown or changing access patterns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;S3 Glacier&lt;/strong&gt;: For long-term archival. Very low storage cost, but retrieving data can take several hours and is even more expensive than Standard-IA.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;S3 Glacier Deep Archive&lt;/strong&gt;: Amazon S3's lowest-cost storage class for long-term archiving where data retrieval times of 12 hours or more are acceptable.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  S3 Pricing Explained
&lt;/h2&gt;

&lt;p&gt;As mentioned above, the different storage classes have different prices. Here are the prices for each S3 storage class:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pricing for S3 Standard&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storage:&lt;/strong&gt; $0.023 per GB for the first 50 TB, $0.022 per GB for the next 450 TB, $0.021 per GB for storage over 500 TB.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access:&lt;/strong&gt; $0.005 per 1000 PUT, COPY, POST, LIST requests. $0.0004 per 1000 GET, SELECT requests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Retrieval:&lt;/strong&gt; $0.00 per GB&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Other charges:&lt;/strong&gt; None&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pricing for S3 Standard-IA (Infrequent Access)&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storage:&lt;/strong&gt; $0.0125 per GB&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access:&lt;/strong&gt; $0.01 per 1000 PUT, COPY, POST, LIST requests. $0.001 per 1000 GET, SELECT requests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Retrieval:&lt;/strong&gt; $0.01 per GB&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Other charges:&lt;/strong&gt; $0.01 per Lifecycle Transition request&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pricing for S3 One Zone-IA&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storage:&lt;/strong&gt; $0.01 per GB&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access:&lt;/strong&gt; $0.01 per 1000 PUT, COPY, POST, LIST requests. $0.001 per 1000 GET, SELECT requests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Retrieval:&lt;/strong&gt; $0.01 per GB&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Other charges:&lt;/strong&gt; $0.01 per Lifecycle Transition request&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 4000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Pricing for S3 Express One Zone&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storage:&lt;/strong&gt; $0.16 per GB&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access:&lt;/strong&gt; $0.0025 per 1000 PUT, COPY, POST, LIST requests. $0.0002 per 1000 GET, SELECT requests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Retrieval:&lt;/strong&gt; $0.00 per GB&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Other charges:&lt;/strong&gt; None&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pricing for S3 Intelligent-Tiering&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storage:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Frequent Access tier: $0.023 per GB for the first 50 TB, $0.022 per GB for the next 450 TB, $0.021 per GB for storage over 500 TB.&lt;br&gt;&lt;br&gt;
Infrequent Access tier: $0.0125 per GB.&lt;br&gt;&lt;br&gt;
Archive Instant Access tier: $0.004 per GB.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access:&lt;/strong&gt; $0.005 per 1000 PUT, COPY, POST, LIST requests. $0.0004 per 1000 GET, SELECT requests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Retrieval:&lt;/strong&gt; $0.00 per GB&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Other charges:&lt;/strong&gt; $0.0025 per 1,000 objects&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pricing for S3 Glacier&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storage:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Instant Retrieval: $0.004 per GB&lt;br&gt;&lt;br&gt;
Flexible Retrieval: $0.0036 per GB&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Instant Retrieval: $0.02 per 1000 PUT, COPY, POST, LIST requests. $0.01 per 1000 GET, SELECT requests.&lt;br&gt;&lt;br&gt;
Flexible Retrieval: $0.03 per 1000 PUT, COPY, POST, LIST requests. $0.0004 per 1000 GET, SELECT requests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Retrieval:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Instant Retrieval: $0.03 per GB&lt;br&gt;&lt;br&gt;
Flexible Retrieval: $0.03 per GB for Expedited, $0.01 per GB for Standard&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Other charges:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Instant Retrieval: $0.02 per Lifecycle Transition request&lt;br&gt;&lt;br&gt;
Flexible Retrieval: $0.03 per Lifecycle Transition request&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pricing for S3 Glacier Deep Archive&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storage:&lt;/strong&gt; $0.00099 per GB&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access:&lt;/strong&gt; $0.05 per 1000 PUT, COPY, POST, LIST requests. $0.0004 per 1000 GET, SELECT requests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Retrieval:&lt;/strong&gt; $0.02 per GB for Standard, $0.0025 per GB for Bulk&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Other charges:&lt;/strong&gt; $0.05 per Lifecycle Transition request&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Pricing Examples for S3 Storage Classes&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To give you a clearer picture of how S3 pricing works, let's see some examples. For each example, assume the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storage:&lt;/strong&gt; 100 GB&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access:&lt;/strong&gt; 100,000 GET requests, 10,000 PUT requests&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Retrieval:&lt;/strong&gt; 100 GB&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example 1: S3 Standard Storage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Storage Cost: $2.30&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Access Cost: $90&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data Retrieval Cost: $0&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Total Cost: $92.30&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example 2: S3 Express One Zone
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Storage Cost: $16&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Access Cost: $200&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data Retrieval Cost: $0&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Total Cost: $216&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example 3: S3 Standard-IA
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Storage Cost: $1.25&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Access Cost: $200&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data Retrieval Cost: $10&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Total Cost: $211.25&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;AWS S3 Free Tier&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;AWS offers a free tier for S3, which includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;5 GB of Standard Storage&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;20,000 GET Requests&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2,000 PUT, COPY, POST, or LIST Requests&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This free tier is a great way to start experimenting with S3 without incurring immediate costs. Also, for really small uses like MVPs you end up paying $0 initially, and your costs only grow as you acquire more users.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Tips for Optimizing AWS S3 Costs&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Understand Your Data Usage&lt;/strong&gt;: Analyze your data access patterns to choose the most cost-effective storage class.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitor Your S3 Billing&lt;/strong&gt;: Regularly check your AWS billing dashboard to track your S3 usage and costs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leverage S3 Lifecycle Policies&lt;/strong&gt;: Automatically move or archive data to lower-cost storage classes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use S3 Analytics&lt;/strong&gt;: Monitor and analyze storage access patterns for cost optimization.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The goal of this guide was to help you understand AWS S3 pricing. Now you're able to use the best storage classes for your use cases, minimizing cost while maintaining durability and availability.&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 4000 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real&lt;/strong&gt; scenarios and solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;why&lt;/strong&gt; behind the solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices&lt;/strong&gt; to improve them&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Subscribe for free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to know more about me, you can find me &lt;a href="https://www.linkedin.com/in/guilleojeda/"&gt;on LinkedIn&lt;/a&gt; or at &lt;a href="https://www.guilleojeda.com?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;www.guilleojeda.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>storage</category>
      <category>cost</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Understanding AWS Lambda Pricing</title>
      <dc:creator>Guille Ojeda</dc:creator>
      <pubDate>Tue, 30 Jan 2024 12:57:00 +0000</pubDate>
      <link>https://forem.com/aws-builders/understanding-aws-lambda-pricing-4pai</link>
      <guid>https://forem.com/aws-builders/understanding-aws-lambda-pricing-4pai</guid>
      <description>&lt;p&gt;In this article, we'll dive deep into the pricing structure of AWS Lambda, breaking down its components, and providing examples to help you understand how costs are calculated. We'll also discuss the AWS Lambda Free Tier and offer practical tips for optimizing your Lambda usage to keep costs manageable.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is AWS Lambda?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;AWS Lambda is a serverless compute service that runs your code in response to events and automatically manages the underlying compute resources for you. This service is capable of executing code in various languages and is commonly used for applications such as web application backends, data processing, and real-time file processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How AWS Lambda Works&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Event-Driven Execution:&lt;/strong&gt; AWS Lambda is designed to run code in response to triggers such as changes in data within AWS services (like S3 or DynamoDB), requests to an API Gateway, or direct invocations via SDKs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automatic Scaling:&lt;/strong&gt; The service scales automatically, executing code in parallel and handling each trigger individually.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexible Resource Allocation:&lt;/strong&gt; Compute power is allocated based on the memory configured for your function, ensuring efficient resource utilization.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Key Components of AWS Lambda&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lambda Functions:&lt;/strong&gt; The core unit where your code resides, along with associated configuration information such as the function name, memory, and timeout settings.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Event Sources:&lt;/strong&gt; These are AWS services or custom sources that trigger your Lambda function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Logs and Monitoring:&lt;/strong&gt; Integration with AWS CloudWatch ensures detailed monitoring and logging of your Lambda functions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Runtime Environments:&lt;/strong&gt; Supports multiple programming languages and runtimes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Understanding AWS Lambda Pricing&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;AWS Lambda's pricing is primarily based on two components: the number of requests your functions process and the compute time they consume. Understanding these components in detail, including their cost, is crucial for effectively managing your AWS Lambda expenses. Here's an expanded breakdown:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Requests:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* **Cost:** AWS Lambda charges $0.20 per 1 million requests.

* **What It Means:** Every time your function is triggered and executed, it counts as a request.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Compute Time:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* &lt;strong&gt;Cost:&lt;/strong&gt; Compute time is charged at $0.00001667 for every GB-second used.

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Calculation:&lt;/strong&gt; The cost is based on the amount of memory allocated to your function and the time it takes to execute.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GB-Second:&lt;/strong&gt; A GB-second is a measure that combines memory usage and execution time. If your function uses 512MB of memory and runs for 3 seconds, it consumes 1.5 GB-seconds (0.5 GB * 3 seconds).&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;strong&gt;AWS Lambda Free Tier&lt;/strong&gt;&lt;br&gt;
&lt;/h3&gt;


&lt;p&gt;AWS offers a generous free tier for Lambda:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;1 million free requests per month.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;400,000 GB-seconds of compute time per month.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pricing Examples for AWS Lambda&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To illustrate how Lambda pricing works, let's consider a few examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Example 1: Low Frequency, Simple Function&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requests: 100,000 in a month&lt;/li&gt;
&lt;li&gt;Duration: Each request runs for 500ms with 128MB memory allocation.&lt;/li&gt;
&lt;li&gt;Total Cost: $0.02 for invocations + $0.1042 for execution time = &lt;strong&gt;$0.1242 / month&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Example 2: High Frequency, Complex Function&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requests: 10 million in a month&lt;/li&gt;
&lt;li&gt;Duration: Each request runs for 800ms with 256MB memory allocation.&lt;/li&gt;
&lt;li&gt;Total Cost: $2.00 for invocations + $33.34 for execution time = &lt;strong&gt;$35.34 / month&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 3600 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Tips for Optimizing AWS Lambda Costs&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitor Function Invocations:&lt;/strong&gt; Regularly review your Lambda function metrics through AWS CloudWatch to understand your usage patterns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Adjust Memory Allocation:&lt;/strong&gt; Optimize the memory allocation for your functions to balance performance and cost.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduce Execution Time:&lt;/strong&gt; Optimize your code to run faster, which directly reduces the compute time cost.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Regularly Review Your Architecture:&lt;/strong&gt; As your application evolves, continually reassess whether your use of Lambda aligns with your operational requirements and cost objectives.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leverage Free Tier:&lt;/strong&gt; Make the most out of the AWS Lambda Free Tier, especially for development and testing purposes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;AWS Lambda offers a flexible, cost-effective solution for running code in response to events. By understanding its pricing model and effectively managing your usage, you can leverage Lambda to build scalable, efficient applications without worrying about infrastructure management.&lt;/p&gt;

&lt;p&gt;The goal of this guide is to help you gain a better understanding of AWS Lambda's pricing structure, enabling you to use this fantastic service more efficiently while keeping your AWS costs manageable.&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 3600 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real&lt;/strong&gt; scenarios and solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;why&lt;/strong&gt; behind the solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices&lt;/strong&gt; to improve them&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Subscribe for free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to know more about me, you can find me &lt;a href="https://www.linkedin.com/in/guilleojeda/"&gt;on LinkedIn&lt;/a&gt; or at &lt;a href="https://www.guilleojeda.com?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;www.guilleojeda.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Disaster Recovery and Business Continuity on AWS</title>
      <dc:creator>Guille Ojeda</dc:creator>
      <pubDate>Tue, 05 Dec 2023 19:45:45 +0000</pubDate>
      <link>https://forem.com/aws-builders/disaster-recovery-and-business-continuity-on-aws-5hc1</link>
      <guid>https://forem.com/aws-builders/disaster-recovery-and-business-continuity-on-aws-5hc1</guid>
      <description>&lt;p&gt;Imagine this scenario: You successfully &lt;a href="https://newsletter.simpleaws.dev/p/data-loss-replication-disaster-recovery-aws?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;replicated your data to another region&lt;/a&gt;, so if your AWS region fails you can still access the data. However, all your servers are still down! You'd like to continue operating even in the event of a disaster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disaster Recovery and Business Continuity
&lt;/h2&gt;

&lt;p&gt;Disasters are events that cause critical damage to our ability to operate as a business. Consider an earthquake near your datacenter (or the ones you're using in AWS), or a flood in that city (this happened to GCP in Paris in the second half of 2023). It follows that Business Continuity is the ability to continue operating (or recovering really fast) in the event of a Disaster. The big question is: &lt;strong&gt;How do we do that?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, let's understand what recovering looks like, and how much data and time can we lose (yes, we lose both) in the process. There are two objectives that we need to set:&lt;/p&gt;

&lt;h2&gt;
  
  
  Recovery Point Objective (RPO)
&lt;/h2&gt;

&lt;p&gt;The RPO is the maximum time that passes between when the data is written to the primary storage and when it's written to the backup. For periodic backups, RPO is equal to the time between backups. For example, if you take a snapshot of your database every 12 hours, your RPO is 12 hours. For continuous replication, the RPO is equal to the replication delay. For example, if you continuously replicate data from the primary storage to a secondary one, the RPO is the delay in that replication.&lt;/p&gt;

&lt;p&gt;Data that hasn't yet been written to the backup won't be available in the event of a disaster, so you want your RPO to be as small as possible. However, minimizing it may require adopting new technologies, which means effort and money. Sometimes it's worth it, sometimes it isn't.&lt;/p&gt;

&lt;p&gt;Different data may require different RPOs. Since the easiness of achieving a low RPO mostly depends on what technologies you use, the decision of what the RPO is for a specific set of data should be considered when selecting where to store it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recovery Time Objective (RTO)
&lt;/h2&gt;

&lt;p&gt;The RTO is the maximum time that can pass from when a failure occurs to when you're operational again. The thing that will have the most impact on RTO is your disaster recovery strategy, which we'll see a bit further down this article. Different technologies will let you reduce the RTO within the same DR strategy, and a technology change may be a good way to reduce RTO without significantly increasing costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stages of a Disaster Recovery Process
&lt;/h2&gt;

&lt;p&gt;These are the three stages that a disaster recovery process goes through, always in this order.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyyrbrq13seuc5sxbhd5x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyyrbrq13seuc5sxbhd5x.png" alt="Stages of a Disaster Recovery Process." width="800" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Detect
&lt;/h3&gt;

&lt;p&gt;Detection is the phase between when the failure actually occurs and when you start doing something about it. The absolute worst way to learn about a failure is from a customer, so detection should be the first thing you automate. The easiest way to do so is through a health check, which is a sample request sent periodically (e.g. every 30 seconds) to your servers. For example, Application Load Balancer implements this to detect whether targets in a target group are healthy, and can raise a CloudWatch Alarm if it has no healthy targets. You can connect that alarm to SNS to receive an email when that happens, and you'd have automated detection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Escalate and Declare
&lt;/h3&gt;

&lt;p&gt;This is the phase from when the first person is notified about an event 🔥 and when the alarm 🚨 sounds and everyone is called to battle stations 🚒. It may involve manually verifying something, or it may be entirely automated. In many cases it happens after a few corrective actions have been attempted, such as rolling back a deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Restore
&lt;/h3&gt;

&lt;p&gt;These are the steps necessary to get a system back online. It may be the old system that we're repairing, or it may be a new copy that we're preparing. It usually involves one or several automated steps, and in some events manual intervention is needed. It ends when the system is capable of serving production traffic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fail over
&lt;/h3&gt;

&lt;p&gt;Once we have a live system capable of serving production traffic, we need to send traffic to it. It sounds trivial, but there are several factors that make it worth being a stage on its own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You usually want to do it gradually, to avoid crashing the new system&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It may not happen instantly (for example, DNS propagation)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sometimes this stage is triggered manually&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You need to verify that it happened&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You continue monitoring afterward&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Disaster Recovery Strategies on AWS
&lt;/h2&gt;

&lt;p&gt;The two obvious solutions to disaster recovery are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://newsletter.simpleaws.dev/p/data-loss-replication-disaster-recovery-aws?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Backing up data to another region&lt;/a&gt; and re-creating the entire system&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Continuously running the system in two regions&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both work, but they're not the only ones. They're actually the two extremes of a spectrum:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Backup and Restore
&lt;/h2&gt;

&lt;p&gt;This is the simplest strategy, and the playbook is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Before an event (and continuously):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Back up all your data to a separate AWS region, which we call the DR region&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;When an event happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Restore the data stores from the backups&lt;/li&gt;
&lt;li&gt;Re-create the infrastructure from scratch&lt;/li&gt;
&lt;li&gt;Fail over to the new infrastructure&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;It's by far the cheapest, all you need to pay for are the backups and any other regional resources that you need to operate (e.g. KMS keys used to encrypt data). When a disaster happens, you restore from the backups and re-create everything.&lt;/p&gt;

&lt;p&gt;I'm being purposefully broad when I say "re-create everything". I bet your infrastructure took you a long time to create. How fast can you re-create it? Can you even do it in hours or a few days, if you can't look at how you did it the first time? (Remember the original region is down).&lt;/p&gt;

&lt;p&gt;The answer, of course, is Infrastructure as Code. It will let you launch a new stack of your infrastructure with little effort and little margin for error. That's why we (and by we I mean anyone who knows what they're doing with cloud infrastructure) insist so much on IaC.&lt;/p&gt;

&lt;p&gt;As you're setting up your infrastructure as code, don't forget about supporting resources. For example, if your CI/CD Pipeline runs in a single AWS Region (e.g. you're using CodePipeline), you'll need to be ready to deploy it to the new region along with your production infrastructure. Other common supporting resources are values stored in Secrets Manager or SSM Parameter Store, KMS keys, VPC Endpoints, and CloudWatch Alarms configurations.&lt;/p&gt;

&lt;p&gt;You can define all your infrastructure as code, but creating the new copy from your templates usually requires some manual actions. You need to document everything, so you're clear on what's the correct order for the different actions, what parameters to use, common errors and how to avoid or fix them, etc. If you have all of your infrastructure defined as code, this documentation won't be really large. However, it's still super important.&lt;/p&gt;

&lt;p&gt;Finally, test everything. Don't just assume that it'll work, or you'll find out that it doesn't right in the middle of a disaster. Run periodic tests for your Disaster Recovery plan, keep the code and the documentation up to date, and keep yourself and your teams sharp.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pilot Light
&lt;/h2&gt;

&lt;p&gt;With Backup and Restore you need to create a lot of things from scratch, which takes time. Even if you cut down all the manual processes, you might spend several hours staring at your terminal or the CloudFormation console waiting for everything to create.&lt;/p&gt;

&lt;p&gt;What's more, most of these resources aren't even that expensive! Things like an Auto Scaling Group are free (without counting the EC2 instances), an Elastic Load Balancer costs only $23/month, and VPC and subnets are free. The largest portion of your costs come from the actual capacity that you use: a large number of EC2 instances, DynamoDB tables with a high capacity, etc. But since most of them are scalable, you could keep all the scaffolding set up with capacity scaled to 0, and scale up in the event of a disaster, right?&lt;/p&gt;

&lt;p&gt;That's the idea behind Pilot Light, and this is the basic playbook:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Before an event (and continuously):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Continuously replicate all your data to a separate AWS region, which we call the DR region&lt;/li&gt;
&lt;li&gt;Set up your infrastructure in the DR region, with capacity at 0&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;When an event happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scale up the infrastructure in the DR region&lt;/li&gt;
&lt;li&gt;Fail over to the DR region&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;One of the things that takes the longest time to create is data stores from snapshots. For that reason, the prescriptive advice (though not a strict requirement) for Pilot Light is to keep data stores functioning, instead of just keeping the backups and restoring from them in a disaster. It is more expensive though.&lt;/p&gt;

&lt;p&gt;Since scaling can be done automatically, the Restore stage is very easy to automate entirely when using Pilot Light. Also, since the scaling times are much shorter than creating everything from scratch, the impact of automating all manual operations will be much higher, and the resulting RTO much lower than with Backup and Restore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Warm Standby
&lt;/h2&gt;

&lt;p&gt;The problem with Pilot Light is that, before it scales, it cannot serve any traffic at all. It works just like the pilot light in a home heater: a small flame that doesn't produce any noticeable heat, but is used to light up the main burner much faster. It's a great strategy, and your users will appreciate that the service interruption is brief, in the order of minutes. But what if you need to serve at least those users nearly immediately?&lt;/p&gt;

&lt;p&gt;Warm Standby uses the same idea as Pilot Light, but instead of remaining at 0 capacity, it keeps some capacity available. That way, if there is a disaster you can fail over immediately and start serving a subset of users, while the rest of them wait until your infrastructure in the DR region scales up to meet the entire production demand.&lt;/p&gt;

&lt;p&gt;Here's the playbook:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Before an event (and continuously):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Continuously replicate all your data to a separate AWS region, which we call the DR region&lt;/li&gt;
&lt;li&gt;Set up your infrastructure in the DR region, with capacity at a percentage greater than 0&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;When an event happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reroute a portion of the traffic to the DR region&lt;/li&gt;
&lt;li&gt;Scale up the infrastructure&lt;/li&gt;
&lt;li&gt;Reroute the rest of the traffic&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;What portion of the traffic you reroute depends on how much capacity you maintain "hot" (i.e. available). This lets you do some interesting things, like setting up priorities where traffic for some critical services is rerouted and served immediately, or even for some premium users.&lt;/p&gt;

&lt;p&gt;It also presents a challenge: How much infrastructure do you keep hot in your DR region? It could be a fixed number like 2 EC2 instances, or you could dynamically adjust this to 20% of the capacity of the primary region (just don't accidentally set it to 0 when the primary region fails!).&lt;/p&gt;

&lt;p&gt;You'd think dynamically communicating to the DR region the current capacity or load of the primary region would be too problematic to bother with. But you should be doing it anyway! When a disaster occurs and you begin scaling up your Pilot Light or Warm Standby infrastructure, you don't want to go through all the hoops of scaling slowly from 0 or low capacity to medium, to high, to maximum. You'd rather go from wherever you are directly to 100% of the capacity you need, be it 30 EC2 instances, 4000 DynamoDB WCUs, or whatever service you're using. To do that, you need to know how much is 100%, or in other words, how much capacity the primary region was running on before it went down. Remember that once it's down you can't go check! To solve that, back up the capacity metrics to the DR region. And once you have them, it's trivial to dynamically adjust your warm standby's capacity.&lt;/p&gt;

&lt;p&gt;You can pick any number or percentage that you want, and it's really a business decision, not a technical one. Just keep in mind that if you pick 0 you're actually using a Pilot Light strategy, and if you pick 100% it's a variation of Warm Standby called Hot Standby, where you don't need to wait until infrastructure scales before rerouting all the traffic.&lt;/p&gt;

&lt;p&gt;An important aspect that Warm Standby introduces is the fact that all three strategies that we've seen so far are active/passive, meaning that one region (the active one) serves traffic, while the other region (the DR one, which is passive) doesn't receive any traffic. With Backup and Restore and with Pilot Light that should be obvious, since they're not able to serve any traffic. Warm Standby is able to serve some traffic, and Hot Standby is able to serve the entirety of the traffic. But even then, they don't get any traffic, and the DR region is passive.&lt;/p&gt;

&lt;p&gt;The reason for this is that, if you allow your DR region to write data while you're using the primary region (i.e. while it isn't down), then you need to deal with distributed databases with multiple writers, which is much harder than a single writer and multiple readers. Some managed services handle this very well, but even then there are implications that might affect your application. For example, DynamoDB Global Tables handle writes in any region where the global table is set up, but they resolve conflicts with a last-writer-wins reconciliation strategy, where if two regions receive write operations for the same item at the same time (i.e. within the replication delay window), the one that was written last is the one that sticks. Not a bad solution, but you don't want to overcomplicate things if you don't have to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-site Active/Active
&lt;/h2&gt;

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

&lt;p&gt;In active/passive configurations, only one region serves traffic. Active/active spreads the traffic across both regions in normal operation conditions (i.e. when there's no disaster). As mentioned in the previous paragraph, this introduces a few problems.&lt;/p&gt;

&lt;p&gt;The main problem is the read/write pattern that you'll use. Distributed data stores with multiple write nodes can experience "&lt;strong&gt;contention&lt;/strong&gt;", a term that means everything is slowed down because multiple nodes are trying to access the same data, and they need to wait for the others so they don't cause inconsistencies. Contention is one of the reasons why databases are hard.&lt;/p&gt;

&lt;p&gt;Another problem is that you're effectively managing two identical but separate infrastructures. Suddenly it's not just a group of instances plus one of everything else (Load Balancer, VPC, etc), but two of everything.&lt;/p&gt;

&lt;p&gt;You also need to duplicate any configuration resources, such as Lambda functions that perform automations, SSM documents, SNS topics that generate alerts, etc.&lt;/p&gt;

&lt;p&gt;Finally, instead of using the same value for "region" in all your code and configurations, you need to use two values, and use the correct one in every case. That's more complexity, more work, more cognitive load, and more chances of mistakes or slip ups.&lt;/p&gt;

&lt;p&gt;Overall, Multi-Site Active/Active is much harder to manage than Warm Standby, but the advantage is that losing a region feels like losing an AZ when you're running a Highly Available workload: You just lose a bit of capacity, maybe fail over a couple of things, but overall everything keeps running smoothly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips for Effective Disaster Recovery on AWS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Decide on a Disaster Recovery Strategy
&lt;/h3&gt;

&lt;p&gt;You can choose freely between any of the four strategies outlined on this article, or you can even choose not to do anything in the event of a disaster. There are no wrong answers, there's only tradeoffs.&lt;/p&gt;

&lt;p&gt;To pick the best strategy for you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Calculate how much money you'd lose per minute of downtime&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If there are hits to your brand image, factor them in as well&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Estimate how often these outages are likely to occur&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Calculate how much each DR strategy would cost&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Determine your RTO for each DR strategy&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Plug everything into your calculator&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make an informed decision&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;"I'd rather be offline for 24 hours once every year and lose $2.000 than increase my anual AWS expenses by $10.000 to reduce that downtime" is a perfectly valid and reasonable decision, but only if you've actually run the numbers and made it consciously.&lt;/p&gt;

&lt;h3&gt;
  
  
  Improve Your Detection
&lt;/h3&gt;

&lt;p&gt;The longer you wait to declare an outage, the longer your users have to wait until the service is restored. On the other hand, a false positive (where you declare an outage when there isn't one) will cause you to route traffic away from a region that's working, and your users will suffer from an outage that isn't there.&lt;/p&gt;

&lt;p&gt;Improving the granularity of your metrics will let you detect anomalies faster. Cross-referencing multiple metrics will reduce your false positives without increasing your detection time. Additionally, consider partial outages, how to differentiate them from total outages, and what the response should be.&lt;/p&gt;

&lt;h3&gt;
  
  
  Practice, Practice, Practice
&lt;/h3&gt;

&lt;p&gt;As with any complex procedure, there's a high probability that something goes wrong. When would you rather find out about it, on regular business hours when you're relaxed and awake, or at 3 am with your boss on the phone yelling about production being down and the backups not working?&lt;/p&gt;

&lt;p&gt;Disaster Recovery involves software and procedures, and as with any software or procedures, you need to test them both. Run periodic disaster recovery drills, just like fire drills but for the prod environment. As the Google SRE book says: "&lt;a href="https://sre.google/sre-book/managing-incidents/"&gt;If you haven’t gamed out your response to potential incidents in advance, principled incident management can go out the window in real-life situations.&lt;/a&gt;"&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Recommended Tools and Resources for Disaster Recovery&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;One of the best things you can read on Disaster Recovery is the &lt;a href="https://docs.aws.amazon.com/whitepapers/latest/disaster-recovery-workloads-on-aws/disaster-recovery-workloads-on-aws.html"&gt;AWS whitepaper about Disaster Recovery&lt;/a&gt;. In fact, it's where I took all the images from.&lt;/p&gt;

&lt;p&gt;Another fantastic read is the chapter about &lt;a href="https://sre.google/sre-book/managing-incidents/"&gt;Managing incidents&lt;/a&gt; from the &lt;a href="https://sre.google/sre-book/table-of-contents/"&gt;Site Reliability Engineering book&lt;/a&gt; (by Google). If you haven't read the whole book, you might want to do so, but chapters stand independently so you can read just this one.&lt;/p&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 3700 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real&lt;/strong&gt; scenarios and solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;why&lt;/strong&gt; behind the solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices&lt;/strong&gt; to improve them&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Subscribe for free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to know more about me, you can find me &lt;a href="https://www.linkedin.com/in/guilleojeda/"&gt;on LinkedIn&lt;/a&gt; or at &lt;a href="https://www.guilleojeda.com?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;www.guilleojeda.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>architecture</category>
    </item>
    <item>
      <title>DynamoDB Transactions: An E-Commerce with Amazon DynamoDB</title>
      <dc:creator>Guille Ojeda</dc:creator>
      <pubDate>Thu, 09 Nov 2023 18:45:14 +0000</pubDate>
      <link>https://forem.com/aws-builders/dynamodb-transactions-an-e-commerce-with-amazon-dynamodb-17me</link>
      <guid>https://forem.com/aws-builders/dynamodb-transactions-an-e-commerce-with-amazon-dynamodb-17me</guid>
      <description>&lt;p&gt;We're building an e-commerce app with DynamoDB for the database, pretty similar to the one we built for the &lt;a href="https://newsletter.simpleaws.dev/p/dynamodb-database-design?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;DynamoDB Database Design article&lt;/a&gt;. No need to go read that issue (though I think it came up great), here's how our database works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Customers are stored with a Customer ID starting with c# (for example c#123) as the PK and SK.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Products are stored with a Product ID starting with p# (for example p#123) as the PK and SK, and with an attribute of type number called 'stock', which contains the available stock.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Orders are stored with an Order ID starting with o# (for example o#123) for the PK and the Product ID as the SK.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When an item is purchased, we need to check that the Product is in stock, decrease the stock by 1 and create a new Order.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Payment, shipping and any other concerns are magically handled by the power of "that's out of scope for this issue" and "it's left as an exercise for the reader".&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are more attributes in all entities, but let's ignore them.&lt;/p&gt;

&lt;p&gt;We're going to use the following AWS services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB:&lt;/strong&gt; A NoSQL database that supports &lt;a href="https://en.wikipedia.org/wiki/ACID" rel="noopener noreferrer"&gt;ACID transactions&lt;/a&gt;, just like any SQL-based database.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Before Implementing DynamoDB Transactions&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We need to read the value of stock and update it atomically. &lt;strong&gt;Atomicity&lt;/strong&gt; is a property of a set of operations, where that set of operations can't be divided: it's either applied in full, or not at all. If we just ran the &lt;code&gt;GetItem&lt;/code&gt; and &lt;code&gt;PutItem&lt;/code&gt; actions separately, we could have a case where two customers are buying the last item in stock for that product, our scalable backend processes both requests simultaneously, and the events go down like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Customer123 clicks Buy&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Customer456 clicks Buy&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instance1 receives request from Customer123&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instance1 executes GetItem for Product111, receives a stock value of 1, continues with the purchase&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instance2 receives request from Customer456&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instance2 executes GetItem for Product111, receives a stock value of 1, continues with the purchase&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instance1 executes PutItem for Product111, sets stock to 0&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instance2 executes PutItem for Product111, sets stock to 0&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instance1 executes PutItem for Order0046&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instance1 receives a success, returns a success to the frontend.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instance2 executes PutItem for Order0047&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instance2 receives a success, returns a success to the frontend.&lt;/p&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fyx3vvlzqrchozywz08x1.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%2Fyx3vvlzqrchozywz08x1.png" alt="The process without transactions" width="800" height="884"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The data doesn't look corrupted, right? Stock for Product111 is 0 (it could end up being -1, depends on how you write the code), both orders are created, you received the money for both orders (out of scope for this issue), and both customers are happily awaiting their product. You go to the warehouse to dispatch both products, and find that you only have one in stock. Where did things go wrong?&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Steps to Implement DynamoDB Transactions&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The problem is that steps 4 and 7 were executed separately, and Instance2 got to read the stock of Product111 (step 6) in between them, and made the decision to continue with the purchase based on a value that hadn't been updated yet, but should have. Steps 4 and 7 need to happen atomically, in a transaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install the AWS SDK
&lt;/h3&gt;

&lt;p&gt;First, install the packages from the &lt;a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/" rel="noopener noreferrer"&gt;AWS SDK V3 for JavaScript&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update the Code to Use Transactions
&lt;/h3&gt;

&lt;p&gt;This is the code in Node.js to run the steps as a transaction (you should add this to the code imaginary you already has for the service):&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/client-dynamodb&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBDocumentClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/lib-dynamodb&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;dynamoDBClient&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;DynamoDBClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;us-east-1&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;dynamodb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBDocumentClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dynamoDBClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//The code imaginary you already has&lt;/span&gt;

&lt;span class="c1"&gt;//This is just some filler code to make this example valid. Imaginary you should already have this solved&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newOrderId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;o#123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;//Must be unique&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p#111&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;//Comes in the request&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;c#123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;//Comes in the request&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transactItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;TransactItems&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;ConditionCheck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SimpleAwsEcommerce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Key&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="nx"&gt;productId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;ConditionExpression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stock &amp;gt; :zero&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;ExpressionAttributeValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:zero&lt;/span&gt;&lt;span class="dl"&gt;'&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="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;Update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SimpleAwsEcommerce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Key&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="nx"&gt;productId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;UpdateExpression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SET stock = stock - :one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;ExpressionAttributeValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:one&lt;/span&gt;&lt;span class="dl"&gt;'&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="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;Put&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SimpleAwsEcommerce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Item&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="nx"&gt;newOrderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;productId&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="p"&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;executeTransaction&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="o"&gt;=&amp;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;const&lt;/span&gt; &lt;span class="nx"&gt;data&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;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transactWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transactItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction succeeded:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&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="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;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;executeTransaction&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;//Rest of the code imaginary you already has&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;After Implementing DynamoDB Transactions&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here's how things may happen with these changes, if both customers click Buy at the same time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Customer123 clicks Buy&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Customer456 clicks Buy&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instance1 receives request from Customer123&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instance2 receives request from Customer456&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Instance1 executes a transaction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ConditionCheck for Product111, stock is greater than 0 (actual value is 1)&lt;/li&gt;
&lt;li&gt;PutItem for Product111, set stock to 0&lt;/li&gt;
&lt;li&gt;PutItem for Order0046&lt;/li&gt;
&lt;li&gt;Transaction succeeds, it's committed.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Instance1 receives a success, returns a success to the frontend.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Instance2 executes a transaction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ConditionCheck for Product111, stock is &lt;strong&gt;not&lt;/strong&gt; greater than 0 (actual value is 0)&lt;/li&gt;
&lt;li&gt;Transaction fails, it's aborted.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Instance2 receives an error, returns an error to the frontend.&lt;/p&gt;&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.amazonaws.com%2Fuploads%2Farticles%2F3v9rr8n53dx7l3ot5g8z.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%2F3v9rr8n53dx7l3ot5g8z.png" alt="The process with transactions" width="800" height="694"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Overview of DynamoDB&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;DynamoDB is so scalable because it's actually a distributed database, where you're presented with a single resource called Table, but behind the scenes there's multiple nodes that store the data and process queries. Data is partitioned using the Partition Key, which is part of the Primary Key (the other part is the Sort Key).&lt;/p&gt;

&lt;p&gt;DynamoDB is highly available (meaning it can continue working if an Availability Zone goes down) because each partition is stored in 3 nodes, each in a separate Availability Zone. This is the "secret" behind DynamoDB's availability and durability. You don't need to know this to use DynamoDB effectively, but now that you do, you see that transactions in DynamoDB are actually distributed transactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How Transactions Work in DynamoDB&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Two-Phase Commit&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;DynamoDB implements distributed transactions using Two-Phase Commit (2PC). This strategy is pretty simple: All nodes are requested to evaluate the transaction to determine whether they're capable of executing it, and only after all nodes report that they're able to successfully execute their part, the central controller sends the order to commit the transaction, and each node does the actual writing, affecting the actual data. For this reason, &lt;strong&gt;all operations done in a DynamoDB transaction consume twice as much capacity&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Itempotency&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;DynamoDB transactions are idempotent. They're identified by an attribute called ClientRequestToken, which the DynamoDB SDK includes automatically on any transactions. If you use the TransactReadItems API or TransactWriteItems API without the SDK, you'll need to include it to achieve transaction idempotency.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Isolation&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Transaction isolation (the I in ACID) is achieved through optimistic concurrency control. This means that multiple DynamoDB transactions can be executed concurrently, but if DynamoDB detects a conflict, one of the transactions will be rolled back and the caller will need to retry the transaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Transactions on Multiple Tables&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;DynamoDB Transactions can span multiple tables, but they can't be performed on indexes. Also, propagation of the data to Global Secondary Indexes and DynamoDB Streams always happens after the transaction, and isn't part of it.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pricing for DynamoDB Transactions&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;There is no direct cost for using transactions. However, all operations performed on DynamoDB as part of a transactions will consume double the amount of capacity units as they regularly would. Write and delete operations consume write capacity, and any condition expression consumes read capacity. This extra capacity is only consumed for the operations on the table, the read and write capacity consumed for updating secondary indexes and for &lt;a href="https://newsletter.simpleaws.dev/p/dynamodb-streams-reacting-to-changes?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;DynamoDB Streams&lt;/a&gt; isn't affected. When working with &lt;a href="https://newsletter.simpleaws.dev/p/dynamodb-scaling-provisioned-on-demand?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;DynamoDB On-Demand Mode&lt;/a&gt;, Request Units are doubled, just like Capacity Units.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;DynamoDB vs SQL databases&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The whole point of this article and the others I've written about DynamoDB is that SQL databases shouldn't be your default. I've shown you that DynamoDB can handle an e-commerce store just fine, including ACID-compliant transactions. That's because for an e-commerce, and in fact for 95% of the applications we write, we can predict data access patterns. When we can do that, we can optimize the structure and relations of a NoSQL database like DynamoDB and have it perform much better than a relational database for those known and predicted access patterns.&lt;/p&gt;

&lt;p&gt;The use case for SQL databases is unknown access patterns! And those come from either giving the user a lot of freedom (which might be a mistake, or might be a core feature of your application), or from doing analytics and ad-hoc queries. In those cases, definitely go for relational databases. Otherwise, see if you can solve it with a NoSQL database like DynamoDB. It'll be much cheaper, and it will scale much better. I'll make one concession though: If all your dev team knows is SQL databases, just go with that unless you have a really strong reason not to.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Using SQL in DynamoDB&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This is gonna blow your mind: You can actually query DynamoDB using SQL! Or more specifically, a SQL-compatible language called &lt;a href="https://aws.amazon.com/blogs/database/a-partiql-deep-dive-understanding-the-language-bringing-sql-queries-to-aws-non-relational-database-services/" rel="noopener noreferrer"&gt;PartiQL&lt;/a&gt;. Amazon developed PartiQL as an internal tool, and it was made generally available by AWS. It can be used on SQL databases, semi-structured data, or NoSQL databases, so long as the engine supports it.&lt;/p&gt;

&lt;p&gt;With PartiQL you could &lt;strong&gt;theoretically&lt;/strong&gt; change your Postgres database for a DynamoDB database without rewriting any queries. In reality, you need to consider all of these points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Why are you even changing? It's not going to be easy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How are you going to migrate all the data?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You need to make sure no queries are triggering a Scan in DynamoDB, because we know those are slow and very expensive. You can &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-iam.html#access-policy-ql-iam-example6" rel="noopener noreferrer"&gt;use an IAM policy to deny full-table Scans&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Again, why are you even changing?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm not saying there isn't a good reason to change, but I'm going to assume it's not worth the effort, and you'll have to prove me otherwise. Remember that replicating the data somewhere else for a different access pattern is a perfectly valid strategy (in fact, that's exactly how DynamoDB GSIs work). We'll discuss it further in a future issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Are there any limitations to using transactions in DynamoDB?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Yes, there are some limitations to using transactions in DynamoDB. Transactions are limited to a maximum of 100 unique items and the total data size within a transaction cannot exceed 4 MB. Additionally, transactions cannot operate on tables with global secondary indexes that have projected attributes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Operational Excellence
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitor transaction latencies:&lt;/strong&gt; Monitor latencies of your DynamoDB transactions to identify performance bottlenecks and address them. Use CloudWatch metrics and AWS X-Ray to collect and analyze performance data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error handling and retries:&lt;/strong&gt; Handle errors and implement exponential backoff with jitter for retries in case of transaction conflicts.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fine-grained access control:&lt;/strong&gt; Assign an IAM Role to your backend with an IAM Policy that only allows the specific actions that it needs to perform, only on the specific tables that it needs to access. You can even do this per record and per attribute. This is least privilege.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Reliability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consider a Global Table:&lt;/strong&gt; You can make your DynamoDB table multi-region using a Global Table. Making the rest of your app multi-region is more complicated than that, but at least the DynamoDB part is easy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance Efficiency
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Optimize provisioned throughput:&lt;/strong&gt; If you're using Provisioned Mode, you'll need to set your Read and Write Capacity Units appropriately. You can also set them to auto-scale, but it's not instantaneous. Remember &lt;a href="https://newsletter.simpleaws.dev/p/sqs-throttle-database-writes-dynamodb?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;the article on using SQS to throttle writes&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cost Optimization
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Optimize transaction sizes:&lt;/strong&gt; Minimize the number of items and attributes involved in a transaction to reduce consumed read and write capacity units. Remember that transactions consume twice as much capacity, so optimizing the operations in a transaction is doubly important.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Stop copying cloud solutions, start &lt;strong&gt;understanding&lt;/strong&gt; them. Join over 3700 devs, tech leads, and experts learning how to architect cloud solutions, not pass exams, with the &lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Simple AWS newsletter&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real&lt;/strong&gt; scenarios and solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;why&lt;/strong&gt; behind the solutions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices&lt;/strong&gt; to improve them&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.simpleaws.dev?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;Subscribe for free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to know more about me, you can find me &lt;a href="https://www.linkedin.com/in/guilleojeda/" rel="noopener noreferrer"&gt;on LinkedIn&lt;/a&gt; or at &lt;a href="https://www.guilleojeda.com?utm_source=blog&amp;amp;utm_medium=dev.to"&gt;www.guilleojeda.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>database</category>
      <category>dynamodb</category>
    </item>
  </channel>
</rss>
