<?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: Vincenzo Marragony</title>
    <description>The latest articles on DEV Community by Vincenzo Marragony (@mrmavin).</description>
    <link>https://dev.to/mrmavin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F260433%2F4819beac-6cb3-4805-9761-472be36291e0.jpeg</url>
      <title>DEV Community: Vincenzo Marragony</title>
      <link>https://dev.to/mrmavin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mrmavin"/>
    <language>en</language>
    <item>
      <title>Saving 62% on EC2 costs ($41k to $15k)</title>
      <dc:creator>Vincenzo Marragony</dc:creator>
      <pubDate>Fri, 28 Apr 2023 14:11:38 +0000</pubDate>
      <link>https://dev.to/mrmavin/saving-62-on-ec2-costs-41k-to-15k-470a</link>
      <guid>https://dev.to/mrmavin/saving-62-on-ec2-costs-41k-to-15k-470a</guid>
      <description>&lt;h1&gt;
  
  
  Scenario
&lt;/h1&gt;

&lt;p&gt;During the first few months of my new job, I created a cloud infrastructure to host a large eCommerce site running on Magento 2. I containerized the app and wrote a Terraform configuration to launch and configure everything needed for the service to work, including VPC, EC2 instances, ECS, S3, CloudFront, EFS, and more.&lt;/p&gt;

&lt;p&gt;The issue with a Magento eCommerce site receiving an average of 10 million requests each month is that the load tends to slow down the infrastructure, requiring a powerful server as a base starting point. After load testing, I found that I need a &lt;code&gt;c5ad.2xlarge&lt;/code&gt; compute-optimized instance with &lt;code&gt;8CPUs&lt;/code&gt; and &lt;code&gt;16GB&lt;/code&gt; of dedicated RAM to run Magento smoothly.&lt;/p&gt;

&lt;p&gt;In addition, Magento requires additional services such as MariaDB, Redis, and Elasticsearch to function. To address this, I’ve chosen to use a dedicated machine for each service. Currently, I am not using RDS due to its pricing, but I’m considering whether the risk of sudden downtime from one of these instances is worth it. &lt;/p&gt;

&lt;p&gt;I’m referring to this risk as we experienced a service disruption in a previous project due to AWS maintenance on one of our running machines. We received an email notifying us of a hardware fault and the need to replace the current instance with another one within two weeks. Unfortunately, AWS shut down the machine immediately after the notice.&lt;/p&gt;

&lt;p&gt;With RDS we could start a database with two running instances, decreasing the risk of downtime in case of hardware failure.&lt;/p&gt;

&lt;p&gt;While this might be worth writing about, let's focus on our cost-saving strategy for now. As of today, it has been two years and we have not experienced any downtime related to these machines in this specific project.&lt;/p&gt;

&lt;h1&gt;
  
  
  Problem
&lt;/h1&gt;

&lt;p&gt;In total, we have a minimum of six instances running in production, including two base instances for ECS, service instances, and a bastion host. The instances are configured by the following sizes: &lt;code&gt;2x c5ad.2xlarge&lt;/code&gt; for ECS, &lt;code&gt;1x m5ad.xlarge&lt;/code&gt; for Redis, &lt;code&gt;1x c5ad.xlarge&lt;/code&gt; for MariaDB, &lt;code&gt;1x c5ad.large&lt;/code&gt; for ElasticSearch, and &lt;code&gt;1x m5a.xlarge&lt;/code&gt; for the bastion host.&lt;/p&gt;

&lt;p&gt;The monthly on-demand costs for each instance are &lt;code&gt;2x $288&lt;/code&gt; for ECS, &lt;code&gt;1x $180&lt;/code&gt; for Redis, &lt;code&gt;1x $144&lt;/code&gt; for MariaDB, &lt;code&gt;1x $72&lt;/code&gt; for ElasticSearch, and &lt;code&gt;1x $180&lt;/code&gt; for the bastion host, totaling &lt;code&gt;$1,152/month&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This amounts to &lt;code&gt;$13,824/year&lt;/code&gt; and &lt;code&gt;$41,472/three years&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;How can we save money from this?&lt;/p&gt;

&lt;h1&gt;
  
  
  Solution
&lt;/h1&gt;

&lt;p&gt;It actually depends on your situation, and in my scenario, I have an eCommerce that is running for more than five years, so it makes sense to get reserved instances for three years paying all upfront.&lt;/p&gt;

&lt;p&gt;Here there’s a Google Sheet with &lt;a href="https://github.com/getstrake/aws-pricing-sheets-addon"&gt;aws-pricing-sheet-addon&lt;/a&gt; to get a quote with the latest pricing, comparing reserved instances with different payment methods.&lt;/p&gt;

&lt;p&gt;Sample picture&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2GoRlwHw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gnnvrhamh86e6a2w6auh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2GoRlwHw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gnnvrhamh86e6a2w6auh.png" alt="Costs Saving Example" width="800" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Read-only URL&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.google.com/spreadsheets/d/1KUOiLJ6odiJv7AbcjrGbj3_Hma7aiuMkJPTi5JnhKYs/edit?usp=sharing"&gt;Costs Evaluation spreadsheet&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Results
&lt;/h1&gt;

&lt;p&gt;With this cost evaluation, I managed to save up to &lt;code&gt;62.40%&lt;/code&gt; in EC2 costs, going from &lt;code&gt;$41,472.00&lt;/code&gt; to &lt;code&gt;$15,593.42&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It’s clear that you will need to pay &lt;code&gt;$15,593.42&lt;/code&gt; all upfront, but depending on your situation, this can be a proficient way to save on cloud costs.&lt;/p&gt;

&lt;p&gt;On a personal level, it was the first time that I’ve spent more than &lt;code&gt;$15.000&lt;/code&gt; in one shot for someone else. It’s two years now that we’re saving and managers are happy about it 😃&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This article is part of a series where I write about production use cases as a cloud engineer. The focus is on meeting business needs, working as a team, and personal growth.&lt;/p&gt;

&lt;p&gt;If you find this article useful or want to share your thoughts about it, feel free to write a comment. I would be happy to read it.&lt;/p&gt;

&lt;p&gt;As a side note, I'd like to add that this article was written with the help of AI.&lt;/p&gt;

&lt;p&gt;See you in the next chapter!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Saving 30% on costs and improve infrastructure reliability with profiling</title>
      <dc:creator>Vincenzo Marragony</dc:creator>
      <pubDate>Mon, 24 Apr 2023 13:13:47 +0000</pubDate>
      <link>https://dev.to/mrmavin/saving-30-on-costs-and-improve-infrastructure-reliability-with-profiling-3hab</link>
      <guid>https://dev.to/mrmavin/saving-30-on-costs-and-improve-infrastructure-reliability-with-profiling-3hab</guid>
      <description>&lt;h1&gt;
  
  
  Scenario
&lt;/h1&gt;

&lt;p&gt;As the technical co-founder of MyStoryViewer, a SaaS product that automated Instagram tasks for customers, I was responsible for full-stack web development and cloud engineering. However, as we began to scale and reached 600 concurrent workers, the product's performance suffered, and our customers began to complain.&lt;/p&gt;

&lt;p&gt;If you want to read more about the underlying infrastructure, I've written &lt;a href="https://dev.to/mrmavin/hundreds-of-bot-instances-to-the-back-end-using-redis-147e"&gt;this post&lt;/a&gt; about how I've managed to set-up workers.&lt;/p&gt;

&lt;h1&gt;
  
  
  Problem
&lt;/h1&gt;

&lt;p&gt;The software responsible for processing customer data was a NodeJS (Typescript) application running concurrently on large servers.&lt;/p&gt;

&lt;p&gt;To troubleshoot the problem of degraded performance, I profiled the application at runtime to determine whether there were any issues causing the degradation.&lt;/p&gt;

&lt;h1&gt;
  
  
  Solution
&lt;/h1&gt;

&lt;p&gt;I started using ClinicJS Doctor to profile my software, and as shown in the results, there were issues with memory usage and event loop delays.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mwxL6oPl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u8f3ybmp0fvk986frlyy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mwxL6oPl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u8f3ybmp0fvk986frlyy.png" alt="NodeJS Clinic Profiling Results" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem was clear: something was using too much memory.&lt;/p&gt;

&lt;p&gt;Since I was also the developer, I knew that a function I had written was retrieving data from the Instagram API. The API provided information about a profile, and I was storing all of the retrieved data in an array. Since I was processing 500 profiles for each iteration, I had megabytes of unnecessary data stored in memory. The solution was to only save relevant data from the API in memory.&lt;/p&gt;

&lt;h1&gt;
  
  
  Results
&lt;/h1&gt;

&lt;p&gt;With a single fix, I managed to cut costs and stop receiving complaints from customers.&lt;/p&gt;

&lt;p&gt;The workers were running in parallel on big servers, and with this improvement in memory usage, I could run more instances per server, reducing costs by around 30%. Additionally, since the workers stopped crashing, I was able to improve infrastructure reliability and customer satisfaction.&lt;/p&gt;

&lt;p&gt;On a personal level, I discovered a new way to troubleshoot and improve performance issues.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This article is part of a series where I write about production use cases as a cloud engineer. The focus is on meeting business needs, working as a team, and personal growth.&lt;/p&gt;

&lt;p&gt;If you find this article useful or want to share your thoughts about it, feel free to write a comment. I would be happy to read it.&lt;/p&gt;

&lt;p&gt;As a side note, I'd like to add that this article was written with the help of AI.&lt;/p&gt;

&lt;p&gt;See you in the next chapter!&lt;/p&gt;

</description>
      <category>profiling</category>
      <category>node</category>
      <category>troubleshooting</category>
      <category>reliability</category>
    </item>
    <item>
      <title>Hundreds of bot instances to the back-end using Redis</title>
      <dc:creator>Vincenzo Marragony</dc:creator>
      <pubDate>Fri, 08 Nov 2019 18:06:23 +0000</pubDate>
      <link>https://dev.to/mrmavin/hundreds-of-bot-instances-to-the-back-end-using-redis-147e</link>
      <guid>https://dev.to/mrmavin/hundreds-of-bot-instances-to-the-back-end-using-redis-147e</guid>
      <description>&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;In this story I'll write about the solution that I've implemented to authenticate and manage more than six hundred instances of a distributed Node application (the bot) to a back-end. At that time I was working for my start-up that was serving an automation tool for Instagram. I needed to constantly exchange data between the bot instances and the associated user profile.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Case Study
&lt;/h1&gt;

&lt;p&gt;The Software as a Service allowed clients to sign-up, configure their working profile and control when to start or stop it. When started, a bot would have taken their configuration parameters and would have started to work on it. For each one of my clients I needed one bot that was processing their data.&lt;/p&gt;

&lt;p&gt;The main challenge that I've faced for such system is to connect a bot to a profile. Who's working to what? I also wanted to understand how many bots were available at any given time, how many of them were currently working and if all the servers had the correct amount of bots running. This way I was able to understand if there was some kind of failure somewhere in the system.&lt;/p&gt;

&lt;p&gt;It was clear that I needed a way to &lt;strong&gt;authenticate&lt;/strong&gt; those bots, a method to update their &lt;strong&gt;state&lt;/strong&gt; and check if they're available to work, a two-way binding to &lt;strong&gt;associate&lt;/strong&gt; a bot to a user profile and a &lt;strong&gt;cronjob&lt;/strong&gt; to check if everything was okay.&lt;/p&gt;

&lt;p&gt;The idea about writing an additional software implementing a client-server model solution seemed to be the best thing to do. A single server instance would have taken the responsibility to manage the bots and the data received by them and the connection to the master database would have done the job in terms of data processing. As you might understand, after managing a front-end written in &lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt;, a back-end written in &lt;a href="https://laravel.com/"&gt;Laravel&lt;/a&gt;, all the DevOps on &lt;a href="https://aws.amazon.com/"&gt;AWS&lt;/a&gt; and a bot written in &lt;a href="https://nodejs.org/"&gt;NodeJS&lt;/a&gt;, I didn't want to add an extra piece to this stack, mostly because I hadn't enough money to hire an additional developer, I had to do it by myself.&lt;/p&gt;

&lt;p&gt;This is why I've still implemented something very similar to a client-server model... using REST APIs!&lt;/p&gt;

&lt;h2&gt;
  
  
  Endpoints
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;/bot/auth&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This endpoint had the responsibility to authenticate the bots. After a successful authentication a &lt;strong&gt;token&lt;/strong&gt; would have been generated and returned by the request. This way the system had something to remember the new bot instance. A single string parameter was required, the &lt;strong&gt;hostname&lt;/strong&gt; of the server hosting it.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;/bot/hertbeat&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This one had the responsibility to give the bot instructions on what to do next. By checking both the bot's own state and the system's state, the back-end was able to understand if there were waiting profiles ready to be assigned, if the current profile has been stopped for whatever reason or if the bot could have kept running. This is why this one was the most critical endpoint: a bot would have called it every thirty seconds.&lt;/p&gt;

&lt;p&gt;Another reason for this endpoint to exists is that after each request, I was saving an extra field to acknowledge that the current bot has done a request here. This way I could check if a bot was still alive or not.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;/bot/push_data&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Finally, this one was used to get data from the bot. There's nothing special to say about it, the required &lt;strong&gt;token&lt;/strong&gt; parameter was what the back-end needed to understand who was the bot sending the data and to which profile was it bound.&lt;/p&gt;

&lt;h2&gt;
  
  
  System health checks
&lt;/h2&gt;

&lt;p&gt;What if a bot crashed for whatever reason? Every instance was built inside a &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; image with supervisor installed and configured to restart the instance in case of unexpected exits. But, you know, when you've got paying clients it's always better to double (or triple) check your job.&lt;/p&gt;

&lt;p&gt;This is why I've thought about a back-end health check. The responsibility for it was delegated to a cron-job that had the task to check the existence for the heartbeat. If it was expired, the bot was considered as dead and deleted from the authenticated bot list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storing bot data using &lt;a href="https://redis.io/"&gt;Redis&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Bots are volatile and the system must be scalable to the infinity. I don't need any permanent storage solution, the RAM is all that I need: high speed and volatility.&lt;/p&gt;

&lt;p&gt;The choice to use &lt;strong&gt;Redis&lt;/strong&gt; has been made because its hashes data sets and the key-value store. The first solution came in handy because given a single hash you can get all the values present inside, very useful to get the state for a single server. On the other hand, the second one has been used to store the last heartbeat by expiring keys.&lt;/p&gt;

&lt;p&gt;Speaking about the bot's data, it has been stored as a JSON string. From its class implementation to the plain object and converted back as needed.&lt;/p&gt;

&lt;p&gt;Commands that have been used are the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://redis.io/commands/hset"&gt;HSET&lt;/a&gt; to save a bot instance, used after successful authentication in &lt;em&gt;/bot/auth&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://redis.io/commands/hget"&gt;HGET&lt;/a&gt; to get a bot instance, mostly used by &lt;em&gt;/bot/heartbeat&lt;/em&gt; and &lt;em&gt;/bot/push_data&lt;/em&gt; endpoints&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://redis.io/commands/hvals"&gt;HVALS&lt;/a&gt; to get all the bots in a server, used by &lt;em&gt;cronjob&lt;/em&gt; and &lt;em&gt;status page&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://redis.io/commands/hdel"&gt;HDEL&lt;/a&gt; used by the &lt;em&gt;cronjob&lt;/em&gt; to delete a bot when dead&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://redis.io/commands/setex"&gt;SETEX&lt;/a&gt; to reset the heartbeat&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://redis.io/commands/exists"&gt;EXISTS&lt;/a&gt; to check if the heartbeat still exists&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Drawing the system
&lt;/h2&gt;

&lt;p&gt;Here's a simple drawing about the structure. Each request interacts with some parts of it and it has been uniquely coloured.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U85Gr-PS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://docs.google.com/drawings/d/e/2PACX-1vS7fl5gpuG4Wj1fAj4PVA3w0ES6TjYT-eSgBE8tKw1l5T-TjS196Kz6Bh7S7Isr4aesvZpnxM2nqyw0/pub%3Fw%3D960%26h%3D720" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U85Gr-PS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://docs.google.com/drawings/d/e/2PACX-1vS7fl5gpuG4Wj1fAj4PVA3w0ES6TjYT-eSgBE8tKw1l5T-TjS196Kz6Bh7S7Isr4aesvZpnxM2nqyw0/pub%3Fw%3D960%26h%3D720" alt="System drawing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Actually coding it
&lt;/h2&gt;

&lt;p&gt;If you wish to see the actual implementation... you're a lucky guy! I've taken part of my PHP application and I've re-written it in NodeJS. Everything is hosted in a GitHub repository. Feel free to have a look at it and play with it. Suggestions are welcome if you see something wrong!&lt;/p&gt;

&lt;p&gt;Repository url: &lt;a href="https://github.com/MrMavin/article-bots-to-backend"&gt;MrMavin/article-bots-to-backend&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What are you going to get when playing with it?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/RiguGfPtK7uEhYfLXB/source.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/RiguGfPtK7uEhYfLXB/source.gif" alt="Bots running"&gt;&lt;/a&gt;___&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank you
&lt;/h3&gt;

&lt;p&gt;If you've got there, thank you! Please take a moment to give me a feedback. I would like to know what do you think about this solution and if you've enjoyed reading this article :)&lt;/p&gt;

</description>
      <category>automation</category>
      <category>redis</category>
      <category>node</category>
    </item>
  </channel>
</rss>
