<?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: Pierre Milliotte</title>
    <description>The latest articles on Forem by Pierre Milliotte (@pmilliotte).</description>
    <link>https://forem.com/pmilliotte</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%2F744044%2F52c4837a-5405-4fb2-b0f4-19db05824178.jpeg</url>
      <title>Forem: Pierre Milliotte</title>
      <link>https://forem.com/pmilliotte</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/pmilliotte"/>
    <language>en</language>
    <item>
      <title>Handle optional input parameters in AWS Step Functions without Lambda</title>
      <dc:creator>Pierre Milliotte</dc:creator>
      <pubDate>Wed, 28 Jun 2023 14:15:57 +0000</pubDate>
      <link>https://forem.com/slsbytheodo/handle-optional-input-parameters-in-aws-step-functions-without-lambda-424m</link>
      <guid>https://forem.com/slsbytheodo/handle-optional-input-parameters-in-aws-step-functions-without-lambda-424m</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you want to apply data processing to an input object in your state machine without the well known struggle of dealing with optional parameters, you can use the &lt;a href="https://github.com/pmilliotte/handle-optional-parameters" rel="noopener noreferrer"&gt;&lt;code&gt;HandleOptionalParameters&lt;/code&gt;&lt;/a&gt; CDK construct as follows:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transformInputValuesTo1ItemArraysTask&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;HandleOptionalParameters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Handle optional parameters&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;requiredProperties&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="s2"&gt;requiredProperty&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;optionalProperties&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="s2"&gt;optionalProperty1&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="s2"&gt;optionalProperty2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="c1"&gt;// Apply States.Array intrinsinc function on every input values:&lt;/span&gt;
      &lt;span class="na"&gt;dataProcessing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;States.Array($.value)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// Other examples:&lt;/span&gt;

      &lt;span class="c1"&gt;// Encode every input values and return the object with same input keys but with encoded values:&lt;/span&gt;
      &lt;span class="c1"&gt;// dataProcessing: "States.Base64Encode($.value)",&lt;/span&gt;

      &lt;span class="c1"&gt;// Concatenate the value with the property name, with the JsonPath $.propertyName:&lt;/span&gt;
      &lt;span class="c1"&gt;// dataProcessing: "States.Format('Current key is {} and associated value is {}.', $.propertyName, $.value)",&lt;/span&gt;
  &lt;span class="p"&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;StateMachine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;State machine using HandleOptionalParameters construct&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;definition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;processObjectValuesTask&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;
  
  
  🔴 Context: AWS Step Functions throws if the provided JsonPath is not present
&lt;/h2&gt;

&lt;p&gt;AWS Step Functions lets you do data processing on a state machine execution input thanks to its &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html" rel="noopener noreferrer"&gt;intrinsic functions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, let's say you need to put every values of an object input in an array: &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%2Fakcejfx44qjo4wuvirja.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%2Fakcejfx44qjo4wuvirja.png" alt="Transform input" width="800" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To transform a value to an 1-item array containing this value with Amazon State Language, you can use the &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html#asl-intrsc-func-arrays" rel="noopener noreferrer"&gt;&lt;code&gt;States.Array()&lt;/code&gt;&lt;/a&gt; intrinsic function:&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%2Fteztq3zoq4vtkp9u0zyp.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%2Fteztq3zoq4vtkp9u0zyp.png" alt="Transform one item object" width="512" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To transform every values of an input object, you can apply &lt;code&gt;States.Array()&lt;/code&gt; on every properties:&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%2Flj2a0vfzu3beczqbcebj.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%2Flj2a0vfzu3beczqbcebj.png" alt="Transform multi items object" width="512" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, when dealing with object inputs, you have to make sure that each property you want to process is present in the input, otherwise the execution will fail ❌:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An error occurred while executing the state 'Transform input values to array' (entered at the event id #2). The function 'States.Array($.optionalProperty)' had the following error: The JsonPath argument for the field '$.optionalProperty' could not be found in the input &lt;code&gt;{ "requiredProperty": 1 }&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Indeed, if &lt;code&gt;foo&lt;/code&gt; is actually undefined:&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%2Fkw5vkecas3hqh12lakz8.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%2Fkw5vkecas3hqh12lakz8.png" alt="Failure if some properties are optional" width="512" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You thus need to handle these optional input parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  🟠 Choices: the false good idea for large inputs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-choice-state.html" rel="noopener noreferrer"&gt;Choice&lt;/a&gt; is another common flow state of AWS Step Functions that enables you to decide on which branch to continue the state machine execution, based on a condition evaluated at run time. You can thus use a Choice state to evaluate the optional property's presence in the input, and apply the &lt;code&gt;States.Array()&lt;/code&gt; function only if the property is defined in the input:&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%2F9a8xe7z9j7e4turvblfn.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%2F9a8xe7z9j7e4turvblfn.png" alt="Avoid failure with choices" width="512" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, if the input has many optional values, your step function may look like this 😓:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5akbfktver4dp0tndzz.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%2Fp5akbfktver4dp0tndzz.png" alt="Choices solution is not scalable" width="512" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This causes scalability issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧑‍💻 &lt;strong&gt;DX:&lt;/strong&gt; as you can see in the execution graph view, debugging is painful in the AWS console&lt;/li&gt;
&lt;li&gt;🛑 &lt;strong&gt;AWS limitations:&lt;/strong&gt; in my case, Cloudformation template hard size limit of 1MB is reached with 10 optional properties&lt;/li&gt;
&lt;li&gt;💸 &lt;strong&gt;Cost:&lt;/strong&gt; in a standard workflow, billing depends on the number of state transition which is proportional to the number of optional properties&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Performance:&lt;/strong&gt; Choices are not parallelised which leads speed to decrease with the number of optional properties&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You thus need to handle the scalability of your optional parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  🟢 Let the &lt;code&gt;HandleOptionalParameters&lt;/code&gt; CDK construct handle the optional parameters for you
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/pmilliotte/handle-optional-parameters" rel="noopener noreferrer"&gt;&lt;code&gt;HandleOptionalParameters&lt;/code&gt;&lt;/a&gt; CDK construct processes every defined values with whatever data processing you provide, while ignoring the undefined ones. And it does it in 9 common flow tasks, regardless of the number of optional parameters 🎉:&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%2Ff0opv8t6eq3tuhocs8ti.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%2Ff0opv8t6eq3tuhocs8ti.png" alt="Handle optional parameters the scalable way" width="512" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's break it down:&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%2Fifl1rpc99wpj4catzuu4.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%2Fifl1rpc99wpj4catzuu4.png" alt="CDK construct explained" width="800" height="1004"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;From now on, stop using Lambda for handling large state machine inputs with many optional parameters ! 😉&lt;/p&gt;

</description>
      <category>welcome</category>
    </item>
    <item>
      <title>Functionless: Can we do (better) without Lambda</title>
      <dc:creator>Pierre Milliotte</dc:creator>
      <pubDate>Wed, 16 Feb 2022 13:38:01 +0000</pubDate>
      <link>https://forem.com/slsbytheodo/functionless-can-we-do-better-without-lambda-4koa</link>
      <guid>https://forem.com/slsbytheodo/functionless-can-we-do-better-without-lambda-4koa</guid>
      <description>&lt;p&gt;As of September 2021, &lt;a href="https://aws.amazon.com/step-functions/" rel="noopener noreferrer"&gt;Step Functions&lt;/a&gt; natively integrates with the AWS SDK, &lt;em&gt;expanding the number of supported AWS Services from 17 to over 200 and AWS API Actions from 46 to over 9,000&lt;/em&gt;. As a result, Step Functions can interact with any service &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;Lambda&lt;/a&gt; integrates with (through the SDK). This opens up a whole new ecosystem of use cases where Step Functions can substitute Lambda.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Step Functions worth substituting Lambda ?
&lt;/h3&gt;

&lt;p&gt;The answer depends on which criterion to compare both services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚀 Performance - which has the smallest response latency ?&lt;/li&gt;
&lt;li&gt;💰 Cost - which is the most cost effective ?&lt;/li&gt;
&lt;li&gt;💻 Developer experience - which is the easiest / fastest / most pleasant to set up and maintain ?&lt;/li&gt;
&lt;li&gt;🔒 Security: which is the most secure ?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;TL;DR This article focuses on performance and shows that Lambda executes on average 3x faster than Step Functions for a specific use case. However, Step Functions' smallest response time overall is the same as Lambda's.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To get these results, I created a lambda and a state machine doing the same tasks (querying from and storing items to &lt;a href="https://aws.amazon.com/dynamodb/" rel="noopener noreferrer"&gt;Dynamodb&lt;/a&gt;), which I then invoked synchronously and successively 1000 times through ApiGateway. &lt;/p&gt;

&lt;p&gt;NB: Since I'm using Step Functions in &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/concepts-standard-vs-express.html" rel="noopener noreferrer"&gt;express mode&lt;/a&gt; - whose pricing is based on Lambda's - I'm also comparing services' cost.&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Setting up the architecture
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Choosing a recurring lambda template to substitute
&lt;/h3&gt;

&lt;p&gt;For this experiment to be meaningful, I wanted to replicate a realistic lambda template, one that I use in my projects. In my &lt;a href="https://martinfowler.com/bliki/CQRS.html" rel="noopener noreferrer"&gt;CQRS&lt;/a&gt; experience, I have repeatedly used Lambda to react to HTTP requests through &lt;a href="https://aws.amazon.com/api-gateway/" rel="noopener noreferrer"&gt;Api Gateway&lt;/a&gt; for storing new events in Dynamodb. As an example, in a bike rental app, I would have used a lambda to react to a "/rent-bike" POST request, in order to:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F547behpaka2btf270rsd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F547behpaka2btf270rsd.png" alt="Lambda workflow"&gt;&lt;/a&gt;&lt;br&gt;Lambda workflow
  &lt;/p&gt;

&lt;p&gt;The code would have looked like this:&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;handler&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;body&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// QUERY EVENTS&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lastEvent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&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;EventStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;consistent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;limit&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="c1"&gt;// CHECK IF EVENTS EXIST&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;lastEvent&lt;/span&gt;&lt;span class="p"&gt;)&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="nx"&gt;createHttpError&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// CHECK FOR CONFLICTS&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;lastEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rented&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;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;createHttpError&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Conflict&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// SAVE A NEW EVENT&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;RentEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lastEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Replicating the lambda behaviour in a state machine
&lt;/h3&gt;

&lt;p&gt;Before the release of AWS SDK integration, Step Functions could only manage 1 item in Dynamodb (&lt;em&gt;getItem&lt;/em&gt;, &lt;em&gt;putItem&lt;/em&gt;, &lt;em&gt;deleteItem&lt;/em&gt;, &lt;em&gt;updateItem&lt;/em&gt;). Since the release, it can now &lt;em&gt;query&lt;/em&gt; multiple items through the AWS SDK. Hence I was able to replicate the "rentBike" lambda behaviour for returning the bike with Step Functions in a "returnBike" state machine: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fl3tygbbc0pkvzzcknsap.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fl3tygbbc0pkvzzcknsap.png" alt="Return bike step function workflow"&gt;&lt;/a&gt;&lt;br&gt;Return bike step function workflow
  &lt;/p&gt;

&lt;p&gt;Once I set up the bike-rental architecture, it was time to compare the lambda vs. state machine performances.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fa1csqwqfhowsg0w6e0tj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fa1csqwqfhowsg0w6e0tj.png" alt="Minimalist bike rental architecture"&gt;&lt;/a&gt;&lt;br&gt;Minimalist bike rental architecture
  &lt;/p&gt;

&lt;h1&gt;
  
  
  2. Comparing services performance
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Querying both resources
&lt;/h3&gt;

&lt;p&gt;I used a &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; collection to query synchronously and successively 1000 times the "/rent-bike" (Lambda) and "/return-bike" (Step Functions) endpoints.&lt;/p&gt;

&lt;p&gt;The following &lt;a href="https://aws.amazon.com/cloudwatch/" rel="noopener noreferrer"&gt;Cloudwatch&lt;/a&gt; widget displays the average integration latency (~65 requests on each resource per minute) of the "rentBike" lambda (in blue) and the "returnBike" state machine (in orange). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F6jgxahoug938oeija0n4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F6jgxahoug938oeija0n4.png" alt="Integration latency"&gt;&lt;/a&gt;&lt;br&gt;Average integration latency per minute
  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Api Gateway integrates on average 3x faster with Lambda  than Step Functions.&lt;/strong&gt; 🤯&lt;/p&gt;

&lt;h3&gt;
  
  
  Splitting the integration latency
&lt;/h3&gt;

&lt;p&gt;To get a better understanding of the performance, I split the integration latency as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F6lo2bvvdn866dfvj3xdk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F6lo2bvvdn866dfvj3xdk.png" alt="Integration latency = API Gateway integration &amp;amp; network latency + Execution duration"&gt;&lt;/a&gt;&lt;br&gt;Integration latency = API Gateway integration &amp;amp; network latency + Execution duration
  &lt;/p&gt;

&lt;p&gt;&lt;u&gt;1) Api Gatway integration &amp;amp; network latency&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F7atcwietfw72yh6ykn5n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F7atcwietfw72yh6ykn5n.png" alt="API Gateway integration &amp;amp; network latency"&gt;&lt;/a&gt;&lt;br&gt;Average API Gateway integration &amp;amp; network latency per minute
  &lt;/p&gt;

&lt;p&gt;There is a 10ms difference between Lambda and Step Functions, which might be due to the difference between Api Gateway &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-api-integration-types.html" rel="noopener noreferrer"&gt;Lambda proxy integration vs. AWS integration&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One way to close the gap could thus be using a &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html" rel="noopener noreferrer"&gt;HTTP api&lt;/a&gt; in which Api Gateway integrations with Lambda and Step Functions are the same, rather than a REST api.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;2) Execution duration&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ff68kmokalmwhuu6eodi6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ff68kmokalmwhuu6eodi6.png" alt="Execution duration"&gt;&lt;/a&gt;&lt;br&gt;Average execution duration per minute
  &lt;/p&gt;

&lt;p&gt;The bulk of the difference (40ms) between Lambda and Step Functions performances lies in resources' execution time.&lt;/p&gt;

&lt;p&gt;It could be explained on the first hand by the memory allocated to both services: &lt;strong&gt;each lambda is allocated 1Go of RAM by default, whereas a state machine is allocated 64Mo&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;On the other hand, I used &lt;a href="https://aws.amazon.com/x-ray/" rel="noopener noreferrer"&gt;X-Ray&lt;/a&gt; to deep dive into execution and response times. The following graphs show the execution duration distributions of the 1000 queries made to both services (⚠️ x-axis scales are different).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F0daz5nzgqivx6k6phzye.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F0daz5nzgqivx6k6phzye.png" alt="Execution duration distribution of 1000 requests"&gt;&lt;/a&gt;&lt;br&gt;Execution duration distribution of 1000 requests
  &lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Besides the only query undergoing a cold start, Lambda distribution highlights that executions behave homogeneously overall: 80% of executions made to Lambda are within 20ms. On the opposite, the different peaks in Step Functions distribution suggest that the state machine benefits from multiple optimisations occurring more sporadically. &lt;/p&gt;

&lt;p&gt;However, when all Step Functions optimisations are gathered, Step Functions performs as well as Lambda: &lt;strong&gt;the best response time for both services overall is 24ms!&lt;/strong&gt;&lt;/p&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Fastest execution&lt;/th&gt;
&lt;th&gt;Slowest Execution&lt;/th&gt;
&lt;th&gt;Integration latency p95&lt;/th&gt;
&lt;th&gt;API Gateway integration + Network latency p95&lt;/th&gt;
&lt;th&gt;Execution duration p95&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;24.0 ms&lt;/td&gt;
&lt;td&gt;745 ms (second slowest is 183ms)&lt;/td&gt;
&lt;td&gt;38.8 ms&lt;/td&gt;
&lt;td&gt;15.0 ms&lt;/td&gt;
&lt;td&gt;23.7 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Step Functions&lt;/td&gt;
&lt;td&gt;24.0 ms&lt;/td&gt;
&lt;td&gt;252 ms&lt;/td&gt;
&lt;td&gt;129.5 ms&lt;/td&gt;
&lt;td&gt;43.1 ms&lt;/td&gt;
&lt;td&gt;86.3 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Performance-wise, Lambda remains a better solution for this use case because, from my understanding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It benefits from better internal optimisations,&lt;/li&gt;
&lt;li&gt;It has a better integration with Api Gateway (on a REST api),&lt;/li&gt;
&lt;li&gt;More resources are allocated to it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, Step Functions is promising:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Its fastest invocation matches Lambda's,&lt;/li&gt;
&lt;li&gt;It also benefits from internal optimisations, although not comparable to Lambda's.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, 1000 successive invocations are not representative of a real application behaviour. I'm curious what Step Functions optimisations a more realistic load test would reveal...&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>performance</category>
      <category>stepfunction</category>
      <category>lambda</category>
    </item>
  </channel>
</rss>
