<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: jordan gonzález</title>
    <description>The latest articles on DEV Community by jordan gonzález (@duncanista).</description>
    <link>https://dev.to/duncanista</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%2F1273335%2F71f64131-5fb7-4ead-a164-626ba8b03379.jpeg</url>
      <title>DEV Community: jordan gonzález</title>
      <link>https://dev.to/duncanista</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/duncanista"/>
    <language>en</language>
    <item>
      <title>Web Scraping with AWS Lambda in Rust</title>
      <dc:creator>jordan gonzález</dc:creator>
      <pubDate>Tue, 04 Feb 2025 04:00:38 +0000</pubDate>
      <link>https://dev.to/aws-builders/web-scraping-with-aws-lambda-in-rust-1lg5</link>
      <guid>https://dev.to/aws-builders/web-scraping-with-aws-lambda-in-rust-1lg5</guid>
      <description>&lt;p&gt;Recently, I have been working on delivering best-in-class observability products for AWS Lambda in Rust. Yet I haven't had the chance to build an actual function using the language. &lt;/p&gt;

&lt;p&gt;For a first project I decided to code a web scraper which would parse the &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html" rel="noopener noreferrer"&gt;AWS docs&lt;/a&gt; and alert me when a new AWS Lambda runtime is supported or deprecated.&lt;/p&gt;




&lt;p&gt;It might feel scary to work with an environment which is not directly provided – unlike NodeJS or Python – but the Rust community is growing rapidly and AWS even maintains other packages besides their SDKs.&lt;/p&gt;

&lt;p&gt;An impressive advantage of using Rust for microservices is that the initialization time will be extremely small, compared to other popular runtimes. As per latest benchmarks from Maxime David, from his popular site &lt;a href="https://maxday.github.io/lambda-perf/" rel="noopener noreferrer"&gt;lambda-perf&lt;/a&gt;, the average for hello-world is sitting at &lt;strong&gt;~14ms&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hello World
&lt;/h2&gt;

&lt;p&gt;Let me first teach you how to easily set up an AWS Lambda in Rust using the vetted tool &lt;code&gt;Cargo Lambda&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cargo-lambda.info/guide/installation.html" rel="noopener noreferrer"&gt;After installing&lt;/a&gt; the tool in your environment, just run the following command and you should be good to go!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cargo lambda new hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You should now have a new directory, which looks very similar to a Cargo project, and your main file should look like this.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.rs&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;lambda_runtime&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;service_fn&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="n"&gt;LambdaEvent&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nd"&gt;#[tokio::main]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;Error&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;let&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;service_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nn"&gt;lambda_runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&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="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;func&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;LambdaEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Value&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;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Error&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;let&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="nf"&gt;.into_parts&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;json!&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Hello Ferris!"&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;Test locally by running the emulator with the &lt;code&gt;watch&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo lambda watch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Invoke in another terminal with the command &lt;code&gt;invoke&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo lambda invoke &lt;span class="nt"&gt;--data-ascii&lt;/span&gt; &lt;span class="s2"&gt;"{ &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;world&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; }"&lt;/span&gt;
&lt;span class="c"&gt;# {"message":"Hello Ferris!"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Simple enough, right? We will dive into deploying to AWS later.&lt;/p&gt;
&lt;h2&gt;
  
  
  Web Scraping
&lt;/h2&gt;

&lt;p&gt;There are multiple ways of scraping the web with Rust, I have chosen to use &lt;a href="https://crates.io/crates/scraper" rel="noopener noreferrer"&gt;scraper&lt;/a&gt; due to the simplicity of my use case.&lt;/p&gt;

&lt;p&gt;I just need to query a page, extract some tables, and see if there have been any changes since my last snapshot.&lt;/p&gt;


&lt;h3&gt;
  
  
  AWS Lambda with HTTP adapter
&lt;/h3&gt;

&lt;p&gt;I will modify the previous example, because I want to be able to trigger this function through a Lambda URL.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo add lambda_http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I’ll separate the handler from the boilerplate needed to execute our HTTP adapter.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.rs&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;lambda_http&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service_fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracing&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="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;http_handler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;http_handler&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[tokio::main]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;tracing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init_default_subscriber&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;service_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now our handler looks more similar to an HTTP API in Rust.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// http_handler.rs&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;lambda_http&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Body&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="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;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;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Error&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;let&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"content-type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"text/html"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&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="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Using &lt;code&gt;scraper&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Moreover, we need to install an HTTP client and our scraper.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo add reqwest scraper
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To start scraping, we simply have to make a request to the site we are extracting the data from, convert that response to text, and then navigate through the document!&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// http_handler.rs&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;lambda_http&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Body&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="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;reqwest&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;header&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HeaderMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;scraper&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Selector&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// … rest of code&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;scrape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AWS_LAMBDA_RUNTIMES_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Get the page as text&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="nf"&gt;.text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Parse the document&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In my case, the elements I care about in the AWS Lambda docs page are the HTML tables, so I need to create a selector for it, and then find the references in the document.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs2w1s8fb83wq1sb1uz8j.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%2Fs2w1s8fb83wq1sb1uz8j.png" alt="HTML table to scrape" width="786" height="740"&gt;&lt;/a&gt;&lt;br&gt;
This is how a row in the table looks like.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// scrape method&lt;/span&gt;

&lt;span class="c1"&gt;// Select the table element&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;table_selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Selector&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="err"&gt;“&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Get the reference to the first table element in the document&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tables_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;table_selector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I will repeat the same process of creating a selector and then using it to get to every row of the table.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// scrape method&lt;/span&gt;

&lt;span class="c1"&gt;// Get the table body&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tbody_selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Selector&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="s"&gt;"tbody"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tbody_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tables_ref&lt;/span&gt;&lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tbody_selector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Get the table rows from the table body&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tr_selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Selector&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="s"&gt;"tr"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tr_elements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tbody_ref&lt;/span&gt;&lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tr_selector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// On every row, filter out empty spaces and breaklines&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tr_elements&lt;/span&gt;&lt;span class="nf"&gt;.for_each&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="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="nf"&gt;.text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="nf"&gt;.trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;.filter&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="py"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;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;These will allow me to have an array of rows as &lt;code&gt;Vec&amp;lt;Vec&amp;lt;String&amp;gt;&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt; 
   &lt;span class="o"&gt;[&lt;/span&gt;
      “Node.js 22”,”nodejs22.x”,”Amazon Linux 2023”,”Not scheduled”,”Not scheduled”,”Not scheduled”
   &lt;span class="o"&gt;]&lt;/span&gt;,
   …
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ihqfxpwv032zj3hjr0i.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%2F3ihqfxpwv032zj3hjr0i.png" alt="Scraped data printed in the terminal" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Deploying with AWS CDK
&lt;/h3&gt;

&lt;p&gt;It is always a good practice to keep our cloud resources controlled through Infrastructure as Code (IaC). AWS provides the Cloud Development Kit (CDK) so developers can manage their resources more efficiently.&lt;/p&gt;

&lt;p&gt;The same organization of developers of &lt;code&gt;cargo-lambda&lt;/code&gt; provide a CDK construct – an abstraction that represents AWS CloudFormation resources and their configuration – for an AWS Lambda developed in Rust.&lt;/p&gt;

&lt;p&gt;In your CDK project, simply install the &lt;code&gt;cargo-lambda-cdk&lt;/code&gt; construct, available in multiple runtimes. Here we will use NPM to install it.&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;cargo-lambda-cdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And just use it in your stack like you would for any other AWS Lambda.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RustFunction&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cargo-lambda-cdk&lt;/span&gt;&lt;span class="dl"&gt;'&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;RustFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rust function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;manifestPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path/to/package/directory/with/Cargo.toml&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;That’s pretty much it. My average initialization time, dependencies included, ended up being around &lt;strong&gt;~40ms&lt;/strong&gt;, while an invocation for processing took an average of ~300ms. Pretty good numbers overall, although I did not invest much in performance here.&lt;/p&gt;

&lt;p&gt;I open sourced my project, so it can be used as a guide to your needs. There I include more processing in order to compare snapshots of scraped data. Please check it out!&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/duncanista/aws-lambda-examples/pull/4" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        [rust] add runtime scraper
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#4&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/duncanista" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F30836115%3Fv%3D4" alt="duncanista avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/duncanista" rel="noopener noreferrer"&gt;duncanista&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/duncanista/aws-lambda-examples/pull/4" rel="noopener noreferrer"&gt;&lt;time&gt;Feb 03, 2025&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;What?&lt;/h1&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Adds a function that scrapes the AWS Lambda docs to check the supported and deprecated runtimes.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;How?&lt;/h1&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Used the following crates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;scraper&lt;/code&gt;: web scraping&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;similar&lt;/code&gt;: to diff data&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prettytable-rs&lt;/code&gt;: to create easy-to-print tables&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lambda_http&lt;/code&gt;: adapter for HTTP triggered AWS Lambdas&lt;/li&gt;
&lt;/ul&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/duncanista/aws-lambda-examples/pull/4" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Setting up Rust in AWS Lambda is far simpler than one might think. Combined with its astonishing start up times, I would say it is a powerful contender for microservices. This does not mean that you have to rewrite everything in Rust.&lt;/p&gt;

&lt;p&gt;This guide is here to demonstrate a simple use case for an element inside a page, but you can build much more complex and powerful scrapers based on this framework. Always make sure to read more and research on your own. I hope this encourages you to explore scraping in Rust!&lt;/p&gt;




&lt;p&gt;🇲🇽 This post is also available in Spanish in my personal blog&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.jordangonzalez.dev/mx/blog/web-scraping-con-aws-lambda-en-rust" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjordangonzalez.dev%2F%2Fstatic%2Fimages%2Ftwitter-card.png" height="478" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.jordangonzalez.dev/mx/blog/web-scraping-con-aws-lambda-en-rust" rel="noopener noreferrer" class="c-link"&gt;
          Web Scraping con AWS Lambda en Rust | Jordan González – Blog
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Aquí aprenderás como hacer web scraping con Rust, el paquete será desplegado en una AWS Lambda, haciendo utilidad de las herramientas de cargo-lambda.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jordangonzalez.dev%2Fstatic%2Ffavicons%2Ffavicon-32x32.png" width="800" height="400"&gt;
        jordangonzalez.dev
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h4&gt;
  
  
  References
&lt;/h4&gt;

&lt;p&gt;[1] David, M. (2025). Lambda Cold Start benchmarks. &lt;a href="https://maxday.github.io/lambda-perf/" rel="noopener noreferrer"&gt;Lambda-perf&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;[2] Cargo Lambda. (2025). Rust functions on AWS Lambda made simple. &lt;a href="https://github.com/cargo-lambda/cargo-lambda" rel="noopener noreferrer"&gt;Cargo Lambda&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;[3] Rust Scraper. (2024) HTML parsing and querying with CSS selectors. &lt;a href="https://github.com/rust-scraper/scraper" rel="noopener noreferrer"&gt;scraper&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;[4] Cargo Lambda. (2024). About CDK Construct to build Rust functions with Cargo Lambda. &lt;a href="https://github.com/cargo-lambda/cargo-lambda-cdk" rel="noopener noreferrer"&gt;Cargo Lambda CDK construct&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>rust</category>
      <category>webscraping</category>
    </item>
    <item>
      <title>Reduce AWS Lambda Cold Starts in .NET</title>
      <dc:creator>jordan gonzález</dc:creator>
      <pubDate>Tue, 26 Nov 2024 17:03:12 +0000</pubDate>
      <link>https://dev.to/aws-builders/reduce-aws-lambda-cold-starts-in-net-4kha</link>
      <guid>https://dev.to/aws-builders/reduce-aws-lambda-cold-starts-in-net-4kha</guid>
      <description>&lt;p&gt;Performance is a key concern for engineers, as it directly impacts spending, user experience, scalability, and reliability. Initialization time falls within this spectrum when working with any Serverless environments. &lt;/p&gt;

&lt;p&gt;When executed for the first time, or after a long break, a Serverless workload requires provisioning resources. This is what we call a cold start. Initialization duration, the time spent initializing code and runtime, is part of the cold start.&lt;/p&gt;

&lt;p&gt;Here’s a great developer guide on &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/execution-environments.html#cold-start-latency" rel="noopener noreferrer"&gt;how AWS defines cold starts for AWS Lambda&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;A few months ago, I was tasked with reducing Datadog’s .NET tracing overhead. The first thing that came to my mind was what my colleague Rey Abolofia did for Python in:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/aws-builders" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&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%2Forganization%2Fprofile_image%2F2794%2F88da75b6-aadd-4ea1-8083-ae2dfca8be94.png" alt="AWS Community Builders " width="350" height="350"&gt;
      &lt;div class="ltag__link__user__pic"&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%2Fuser%2Fprofile_image%2F1157940%2F01a21ca7-0d67-4104-995b-39983cdd95c6.png" alt="" width="370" height="370"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/aws-builders/reducing-aws-lambda-cold-starts-3eea" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Reducing AWS Lambda Cold Starts&lt;/h2&gt;
      &lt;h3&gt;Rey Abolofia for AWS Community Builders  ・ May 24&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#coldstart&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#aws&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#serverless&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;So I thought, since .NET is a framework which requires C# to be compiled, there has to be a way to reduce the amount of work being done during runtime. After reading more about how .NET compilation works, I set out to compile our tracer ahead of time.&lt;/p&gt;

&lt;p&gt;As a result, I achieved  a &lt;strong&gt;25% performance improvement in cold starts&lt;/strong&gt;. Let me explain how I accomplished this.&lt;/p&gt;

&lt;h1&gt;
  
  
  .NET Compilation
&lt;/h1&gt;

&lt;p&gt;Understanding how the .NET framework works is crucial, it will allow you to improve how your code is delivered. Many engineers overlook this aspect, mainly because they prioritize shipping code – but investing time in understanding how it works will pay dividends over time, as there are optimizations one can miss without this crucial knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Default Compilation
&lt;/h2&gt;

&lt;p&gt;.NET applications are compiled into a language-agnostic Common Intermediate Language (CIL). Compiled code is stored in assemblies: files with a &lt;code&gt;.dll&lt;/code&gt; or &lt;code&gt;.exe&lt;/code&gt; file extension.&lt;/p&gt;

&lt;p&gt;During runtime, the Common Language Runtime (CLR) is in charge of taking the assemblies and using a Just-In-Time (JIT) compiler to turn the Intermediate Language code into native code for the local machine to run. [1]&lt;/p&gt;

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

&lt;p&gt;So, even though .NET applications are required to be compiled, there’s another compilation step during runtime – which requires compute power, and which subsequently translates into execution time.&lt;/p&gt;

&lt;p&gt;There are two main techniques on how we can improve our Cold Starts, but the main idea behind them is Ahead-Of-Time (AOT) compilation. One is ReadyToRun, and the other one is Native AOT.&lt;/p&gt;




&lt;h2&gt;
  
  
  ReadyToRun
&lt;/h2&gt;

&lt;p&gt;ReadyToRun (R2R) is a form of ahead-of-time (AOT) compilation. The binaries produced improve the startup performance by reducing the amount of work that the JIT compiler needs to do as our application loads. [2]&lt;/p&gt;

&lt;p&gt;The main disadvantage is that &lt;strong&gt;R2R binaries are much larger because they contain both IL code and the native version of the same code&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Native AOT
&lt;/h2&gt;

&lt;p&gt;Native AOT compilation produces an app that has been ahead-of-time compiled into native code for a specific architecture. Therefore, these applications will not use the JIT compiler during runtime. Not only will they have a &lt;strong&gt;faster startup time&lt;/strong&gt;, but also a &lt;strong&gt;smaller memory footprint&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Another great advantage is that these binaries do not require the local machine to have the .NET runtime installed at all. Although a limitation is that &lt;strong&gt;you cannot cross-compile&lt;/strong&gt;. [3]&lt;/p&gt;




&lt;h1&gt;
  
  
  Choosing a Compilation Strategy
&lt;/h1&gt;

&lt;p&gt;The easy pick would be to compile with Native AOT all the time, right? Because it doesn’t require the .NET runtime, nor a JIT compiler. Unfortunately, there will be scenarios which you simply cannot do. [4]&lt;/p&gt;

&lt;p&gt;For example, if you are doing dynamic loading, through &lt;code&gt;Assembly.LoadFile&lt;/code&gt;, or runtime code generation using reflection, with &lt;code&gt;System.Reflection.Emit&lt;/code&gt;, when compiling to Native AOT, you will find warnings during the process and your app will behave unexpectedly.&lt;/p&gt;

&lt;p&gt;In my specific task, I couldn’t take advantage of Native AOT compilation because the Datadog .NET tracer uses dynamic loading and reflection. Due to the amount of required changes needed for this to work, I had to settle with R2R until we update the tracer.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  R2R
&lt;/h3&gt;

&lt;p&gt;To enable ReadyToRun compilation, simply add the following property in your &lt;code&gt;.csproj&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Project&lt;/span&gt; &lt;span class="na"&gt;Sdk=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.NET.Sdk"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- ...other properties --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;PublishReadyToRun&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/PublishReadyToRun&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Native AOT
&lt;/h3&gt;

&lt;p&gt;For Native AOT compilation, you can set the property, also in your &lt;code&gt;.csproj&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;PublishAot&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/PublishAot&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To ensure that your application is Native AOT compatible, you can set this property in the same file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;IsAotCompatible&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/IsAotCompatible&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h1&gt;
  
  
  Benchmarks
&lt;/h1&gt;

&lt;p&gt;To get data around cold starts, the methodology I used is simple: force a new sandbox for AWS Lambda every certain point in time, and emit telemetry by using an observability tool. &lt;/p&gt;

&lt;p&gt;If you want a quick project to quick start and try it out for yourself, go to my &lt;a href="https://github.com/duncanista/aws-lambda-examples" rel="noopener noreferrer"&gt;example repository&lt;/a&gt; which uses the AWS CDK to benchmark a Hello World app with these strategies.&lt;/p&gt;
&lt;h3&gt;
  
  
  Hello World
&lt;/h3&gt;

&lt;p&gt;For a simple AWS Lambda serializing an API Gateway HTTP payload and returning it, we see almost no benefits when using R2R, at around &lt;code&gt;~10ms&lt;/code&gt; removed. But &lt;strong&gt;when compiling to Native AOT&lt;/strong&gt;, we can see an &lt;strong&gt;improvement of 75%&lt;/strong&gt;, with around &lt;code&gt;~400ms&lt;/code&gt; being saved.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw9sgg42fb2xgmtwk27t8.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%2Fw9sgg42fb2xgmtwk27t8.png" alt="Graphic comparing .NET compilation methods for Arm64 where Native AOT is considerably faster than R2R and the default." width="800" height="293"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Due to lack of cross-compilation, I couldn't show the data for x86_64 for this test.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7uvs5iyitoq3s0eae5vb.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%2F7uvs5iyitoq3s0eae5vb.png" alt="Graphic showing .NET compilations with R2R and default only for x86_64 architectures" width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Datadog Tracer
&lt;/h3&gt;

&lt;p&gt;For an immense codebase like the &lt;a href="https://github.com/DataDog/dd-trace-dotnet" rel="noopener noreferrer"&gt;Datadog .NET tracer&lt;/a&gt;, publishing a release with ReadyToRun enabled improved positively the performance, as said before, a 25% cut during initialization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4fardlt4ypvyul8xodu.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%2Fm4fardlt4ypvyul8xodu.png" alt="Graphic showing .NET compilations with R2R and default only for the Datadog Tracer" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This code is publicly available, feel free to check it out in &lt;a href="https://github.com/DataDog/dd-trace-dotnet/pull/5962" rel="noopener noreferrer"&gt;DataDog/dd-trace-dotnet#5962&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/DataDog/dd-trace-dotnet/pull/5962" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        [build] Build tracer with ReadyToRun
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#5962&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/duncanista" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F30836115%3Fv%3D4" alt="duncanista avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/duncanista" rel="noopener noreferrer"&gt;duncanista&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/DataDog/dd-trace-dotnet/pull/5962" rel="noopener noreferrer"&gt;&lt;time&gt;Aug 29, 2024&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Summary of changes&lt;/h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Allows tracer publishing to be compiled with &lt;a href="https://learn.microsoft.com/en-us/dotnet/core/deploying/ready-to-run" rel="nofollow noopener noreferrer"&gt;ReadyToRun&lt;/a&gt; to improve Serverless workloads init duration.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Reason for change&lt;/h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;It has showcased a 500ms init duration improvement for AWS Lambda. Potentially could be used for other workloads in the future.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Implementation details&lt;/h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Followed #4573 and &lt;a href="https://learn.microsoft.com/en-us/dotnet/core/deploying/ready-to-run" rel="nofollow noopener noreferrer"&gt;ReadyToRun&lt;/a&gt; docs.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Test coverage&lt;/h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;TBD&lt;/li&gt;
&lt;li&gt;Tested manually in AWS Lambda.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Other details&lt;/h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Increases tracer size by 3x.&lt;/p&gt;


    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/DataDog/dd-trace-dotnet/pull/5962" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;In general, understanding how the compiler works will open a lot of doors for you to become a better engineer, and give you the foundational knowledge to think of ways to improve your applications’ performance.&lt;/p&gt;

&lt;p&gt;The clear benefit of &lt;strong&gt;applying this in Serverless workloads&lt;/strong&gt; is that your applications &lt;strong&gt;will be able to serve faster&lt;/strong&gt;, and &lt;strong&gt;save money&lt;/strong&gt; at the same time.&lt;/p&gt;

&lt;p&gt;For more improvements, like stripping and trimming, I'd recommend deep diving into the referenced content and the AWS developer guide to &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/dotnet-native-aot.html" rel="noopener noreferrer"&gt;compile .NET into Native AOT&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;🇲🇽 This post is also available in Spanish in my personal blog&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.jordangonzalez.dev/mx/blog/reduce-cold-starts-en-dotnet-para-aws-lambda" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjordangonzalez.dev%2F%2Fstatic%2Fimages%2Ftwitter-card.png" height="478" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.jordangonzalez.dev/mx/blog/reduce-cold-starts-en-dotnet-para-aws-lambda" rel="noopener noreferrer" class="c-link"&gt;
          Reduce Cold Starts en .NET para AWS Lambda | Jordan González – Blog
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Aprende como mejorar drasticamente el rendimiento de tus aplicaciones .NET durante un inicio en frío en AWS Lambda, o cualquier otro entorno sin servidor.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jordangonzalez.dev%2Fstatic%2Ffavicons%2Ffavicon-32x32.png" width="800" height="400"&gt;
        jordangonzalez.dev
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;





&lt;h4&gt;
  
  
  References
&lt;/h4&gt;

&lt;p&gt;Thanks to &lt;strong&gt;Lucas Pimentel&lt;/strong&gt;, who explained to me that this was possible.&lt;/p&gt;

&lt;p&gt;&lt;a id="R1"&gt;&lt;/a&gt;[1] Microsoft. (2024). What is .NET Framework: Architecture of .NET Framework. &lt;a href="https://dotnet.microsoft.com/en-us/learn/dotnet/what-is-dotnet-framework#:~:text=drawing%2C%20and%20more.-,.,computer%20it%20is%20running%20on." rel="noopener noreferrer"&gt;Microsoft Learn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="R2"&gt;&lt;/a&gt;[2] davidwrighton, gewarren, &amp;amp; Miskelly. (2022, June). ReadyToRun Compilation. &lt;a href="https://learn.microsoft.com/en-us/dotnet/core/deploying/ready-to-run" rel="noopener noreferrer"&gt;Microsoft Learn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="R3"&gt;&lt;/a&gt;[3] LakshanF et al. (2024, October 15). Native AOT Deployment. &lt;a href="https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot" rel="noopener noreferrer"&gt;Microsoft Learn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="R4"&gt;&lt;/a&gt;[4] stevewhims &amp;amp; mattwojo (2022, October). .NET Native and compilation. &lt;a href="https://learn.microsoft.com/en-us/windows/uwp/dotnet-native/net-native-and-compilation" rel="noopener noreferrer"&gt;Microsoft Learn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>dotnet</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
