<?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: Jordi Been</title>
    <description>The latest articles on DEV Community by Jordi Been (@jordibeen).</description>
    <link>https://dev.to/jordibeen</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%2F1177599%2F872d1e32-1c05-4cf8-9374-eab1ebdc34a2.jpg</url>
      <title>DEV Community: Jordi Been</title>
      <link>https://dev.to/jordibeen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jordibeen"/>
    <language>en</language>
    <item>
      <title>From Chaos to Control: The Importance of Tailored Autoscaling in Kubernetes</title>
      <dc:creator>Jordi Been</dc:creator>
      <pubDate>Wed, 14 Aug 2024 08:58:35 +0000</pubDate>
      <link>https://dev.to/check/from-chaos-to-control-the-importance-of-tailored-autoscaling-in-kubernetes-2kpn</link>
      <guid>https://dev.to/check/from-chaos-to-control-the-importance-of-tailored-autoscaling-in-kubernetes-2kpn</guid>
      <description>&lt;p&gt;Autoscaling in Kubernetes (k8s) is hard to get right. There are a lot of different autoscaling tools and flavors to choose from, while at the same time, each application demands a different set of resources. So, unfortunately, there's no 'one size fits all' implementation for autoscaling. A custom configuration that's tailor-made to the type of application you're hosting is often the best bet.&lt;/p&gt;

&lt;p&gt;At Check, it took us a few iterations until we found the ideal configuration for our main API. The optimal solution required us to not only configure Kubernetes correctly but also tweak some settings in the k8s Deployment for it to work perfectly.&lt;/p&gt;

&lt;p&gt;In this blog post, we'd like to share some of the challenges we faced and mistakes we made, so that you don't have to make them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Autoscaling in Kubernetes: Choosing the Right Tool for Your Deployment
&lt;/h2&gt;

&lt;p&gt;The right cluster-based autoscaling configuration is highly dependent on the type of Deployment you're hosting, and using the right tools for the job. There are several types of autoscaling tools to choose from when using Kubernetes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scaling Deployments
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Horizontal Pod Autoscaling
&lt;/h4&gt;

&lt;p&gt;A Horizontal Pod Autoscaler (HPA) dynamically adjusts the number of Pods in a Deployment to match changing workload demands. When traffic increases, the HPA scales up by deploying more Pods. Conversely, when demand decreases, it scales back down.&lt;/p&gt;

&lt;h4&gt;
  
  
  Vertical Pod Autoscaling
&lt;/h4&gt;

&lt;p&gt;A Vertical Pod Autoscaler (VPA) automatically sets resource limits and requests based on usage patterns. This improves scheduling efficiency in Kubernetes by only allocating resources to nodes that have sufficient capacity. VPA can also downscale Pods that are over-requesting resources and upscale those that are under-requesting them.&lt;/p&gt;

&lt;h4&gt;
  
  
  KEDA
&lt;/h4&gt;

&lt;p&gt;For more complex use cases, you can leverage the &lt;a href="https://keda.sh/" rel="noopener noreferrer"&gt;Kubernetes Event Driven Autoscaler (KEDA)&lt;/a&gt; to scale Deployments based on external events. This allows you to scale according to a Cron schedule, database queries (PostgreSQL, MySQL, MSSQL), or items in an event queue (Redis, RabbitMQ, Kafka).&lt;/p&gt;

&lt;h3&gt;
  
  
  Scaling Nodes
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Cluster Autoscaling
&lt;/h4&gt;

&lt;p&gt;A Cluster Autoscaler automatically manages Node scaling by adding Nodes when there are unschedulable Pods and removing them when possible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Scaling Our Main API
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Unpredictable Nature of Our Traffic
&lt;/h3&gt;

&lt;p&gt;As a shared mobility operator in The Netherlands, our main API's traffic is directly tied to the actual traffic in cities. It's not uncommon for us to see a significant spike in requests during rush hour - we're talking 100K requests per 5 minutes! On the other hand, weekdays at midnight are a different story, with only around 5-10K requests per 5 minutes. And then there are the weekends, which can be highly unpredictable due to weather conditions.&lt;/p&gt;

&lt;p&gt;With such enormous differences in load, it's impossible to account for manually - especially when you factor in surprising spikes and peak loads. That's where k8s autoscaling comes in, saving our lives (and sanity!) by automatically scaling our resources to match demand.&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%2F9no93ogg1xs9064wy6zn.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%2F9no93ogg1xs9064wy6zn.png" alt="Graph showing API traffic fluctuation in response to Dutch city traffic demand" width="800" height="181"&gt;&lt;/a&gt;&lt;em&gt;Graph showing API traffic fluctuation in response to Dutch city traffic demand&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Our Use Case: HPA + Cluster Autoscaler
&lt;/h3&gt;

&lt;p&gt;For our use case, we found that a Horizontal Pod Autoscaler (HPA) combined with a Cluster Autoscaler was the perfect solution. During rush hour, the HPA scales up our Deployment to meet demand, spinning up more Pods as needed. When there aren't enough resources available on running Nodes, the Cluster Autoscaler kicks in, automatically adding new Nodes to the mix.&lt;/p&gt;

&lt;p&gt;When traffic dies down, the HPA scales back down to a manageable level, after which the Cluster Autoscaler removes unnecessary Nodes. This automated scaling has been a game-changer for us, allowing us to focus on other important tasks while our infrastructure takes care of itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge of Unpredictable Deployments
&lt;/h2&gt;

&lt;p&gt;As we delved into the world of Kubernetes autoscaling, we encountered a difficult challenge to overcome. Kubernetes' autoscaling tools depend on the retrieval of metrics. For resource metrics, this is the &lt;code&gt;metrics.k8s.io&lt;/code&gt; API, provided by the &lt;a href="https://github.com/kubernetes-sigs/metrics-server" rel="noopener noreferrer"&gt;Kubernetes Metrics Server&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We tried to understand our Deployment's behavior by analyzing its resource usage in Grafana. However, we soon realized that the amount of memory used by each Pod was fluctuating wildly. Because our Deployment's resource usage was behaving unpredictably, it made it very difficult to configure our resources correctly for autoscaling.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Eye Opener
&lt;/h4&gt;

&lt;p&gt;While developing one of our microservices built in FastAPI, we stumbled upon a crucial piece of documentation that highlighted the importance of handling replication at the cluster level rather than using using process managers like Gunicorn in each container.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“If you have a cluster of machines with Kubernetes [...] then you will probably want to handle replication at the cluster level instead of using a process manager (like Gunicorn with workers) in each container.”&lt;br&gt;
  &lt;a href="https://fastapi.tiangolo.com/deployment/docker/#replication-number-of-processes" rel="noopener noreferrer"&gt;"Replication - Number of Processes"&lt;/a&gt; (FastAPI documentation)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This was a real eye-opener for us!&lt;/p&gt;

&lt;h4&gt;
  
  
  Gunicorn Workers Causing Confusion
&lt;/h4&gt;

&lt;p&gt;Check's main API was originally built in &lt;a href="https://docs.pylonsproject.org/projects/pyramid/en/latest/" rel="noopener noreferrer"&gt;Pyramid&lt;/a&gt;, a Python web framework. Just like Django, Pyramid projects are typically served as a WSGI callable using a WSGI HTTP Server such as Gunicorn. Our legacy configuration had Gunicorn set to use 4 workers at all times.&lt;/p&gt;

&lt;p&gt;On &lt;a href="https://docs.gunicorn.org/en/stable/design.html#how-many-workers" rel="noopener noreferrer"&gt;Gunicorn's documentation page&lt;/a&gt;, they strongly advise running multiple workers, recommending &lt;em&gt;"(2 x $num_cores) + 1 as the number of workers to start off with"&lt;/em&gt; and seemingly incentivizing users to use as many workers as possible.&lt;/p&gt;

&lt;p&gt;As we dug deeper into the issue, we realized that Gunicorn's load balancing across multiple worker processes was now confusing the Kubernetes Metrics Server API. Because a single Pod had 4 different workers actively processing requests, the resources it used would vary greatly according to the types of operations it was handling at the same time.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: A Single Process Per Pod
&lt;/h3&gt;

&lt;p&gt;After this revelation, we moved to a single Gunicorn worker per Pod and saw immediate positive results.&lt;/p&gt;

&lt;p&gt;Even though we now had to run close to 4 times as many Pods, we were able to dumb down the Deployment's resource configuration, ultimately causing a single Pod to run with significantly fewer resources too!&lt;/p&gt;

&lt;p&gt;When analyzing the behavior of individual pods in Grafana after these changes, it revealed fewer memory spikes, with each Pod staying close to its average resource usage. Most importantly, our HPA started doing its job correctly!&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%2F9s7sm3ntu0b6351faspl.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%2F9s7sm3ntu0b6351faspl.png" alt="Graph showing pods spinning up in response to increased demand" width="800" height="279"&gt;&lt;/a&gt;&lt;em&gt;Graph showing Pods spinning up in response to increased demand&lt;/em&gt;&lt;/p&gt;




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

&lt;p&gt;Kubernetes autoscaling can be a complex beast, but with the right approach, it can bring significant benefits to your Deployment. As we navigated the world of Kubernetes autoscaling, we learned some valuable lessons.&lt;/p&gt;

&lt;h3&gt;
  
  
  Analyze and Understand
&lt;/h3&gt;

&lt;p&gt;Thorough analysis is key when configuring cluster-based autoscaling with Kubernetes. By understanding your Deployment's resource usage patterns, you can set the right limits for individual Pods and ensure that your cluster autoscaler is working effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoid Metrics-Server Confusion
&lt;/h3&gt;

&lt;p&gt;When using WSGI tools like Gunicorn, be aware of their internal load-balancing features. These can confuse the metrics-server and lead to incorrect scaling decisions. To avoid this, configure your container image in such a way that it can be correctly scaled by the cluster instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tailoring Your Solution
&lt;/h3&gt;

&lt;p&gt;Most importantly, find the right combination of tools and resource configuration that suits your unique deployment needs. We've found how a HPA (Horizontal Pod Autoscaler) worked well for our main API deployment, while a Cron-based autoscaler was more suitable for scaling up our deployment that generates invoices on the first day of the month.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Payoff: Reduced Costs and Improved Peace of Mind
&lt;/h3&gt;

&lt;p&gt;By correctly configuring cluster-based autoscaling, we were able to reduce costs and improve peace of mind. Our Deployment now automatically scales according to traffic on our API, eliminating the need for manual server capacity reconfigurations.&lt;/p&gt;

&lt;p&gt;Even though getting to a feasible situation isn't easy, it's well worth the time spent. And, as is often the case with technical concepts, you'll improve your feel for configuring these relatively new tools as you start using them more. With each new autoscaling setup, you'll gain more confidence in translating Grafana dashboards into HPA configurations, making it easier to configure autoscaling for your future deployments one step at a time.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>autoscaling</category>
      <category>devops</category>
      <category>cloudcomputing</category>
    </item>
    <item>
      <title>How having one million API requests an hour pointed us into building a Rust microservice that processes fleet updates</title>
      <dc:creator>Jordi Been</dc:creator>
      <pubDate>Tue, 30 Jan 2024 11:05:43 +0000</pubDate>
      <link>https://dev.to/check/how-having-one-million-api-requests-an-hour-pointed-us-into-building-a-rust-microservice-that-processes-fleet-updates-2d64</link>
      <guid>https://dev.to/check/how-having-one-million-api-requests-an-hour-pointed-us-into-building-a-rust-microservice-that-processes-fleet-updates-2d64</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;EVERYONE IN THE CITY, EVERYWHERE IN 15 MINUTES.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's our motto at &lt;a href="https://ridecheck.app/en" rel="noopener noreferrer"&gt;Check Technologies&lt;/a&gt;, a shared mobility operator in The Netherlands where users can rent e-mopeds, e-kickscooters or e-cars. When founding the company, Check decided to hire a team of engineers to build a custom platform, as opposed to using an off-the-shelf SaaS product. &lt;/p&gt;

&lt;p&gt;This team of just 6 engineers are responsible for not only building, maintaining and improving the Check application used by over 800K users today, but also building upon internal tooling, performing data analyses and taking care of hosting of the platform.&lt;/p&gt;

&lt;p&gt;From launching back in February 2020 until now, the company has seen significant growth in users, trips and vehicles.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Amount of Vehicles&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 Jan 2021&lt;/td&gt;
&lt;td&gt;1170&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 Jan 2022&lt;/td&gt;
&lt;td&gt;3146&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 Jan 2023&lt;/td&gt;
&lt;td&gt;8160&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With this new blog, we (Check's engineering team) would like to share some of the technical challenges we had to overcome, the solutions we came up with, and the insights we've gained along the way. Expect write-ups from different engineers within the team who will share their thoughts on topics related to their domain, such as app development, cloud infrastructure and data engineering.&lt;/p&gt;

&lt;p&gt;First up: &lt;strong&gt;How having one million API requests an hour pointed us into building a Rust microservice that processes fleet updates&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  A microservice, why?
&lt;/h2&gt;

&lt;p&gt;Up until the start of 2022, the Check backend was hosted as an &lt;a href="https://aws.amazon.com/elasticbeanstalk/" rel="noopener noreferrer"&gt;Elastic Beanstalk&lt;/a&gt; web application. Even though this AWS service proved to be reliable for getting us off the ground initially, we had run into its limits multiple times. Getting the right autoscaling configuration was rough, the costs were growing month after month and most importantly: it's not made for hosting a microservice infrastructure.&lt;/p&gt;

&lt;p&gt;By making the move to Kubernetes starting that year, we paved the way for building smaller applications that can run (and scale) independently. &lt;em&gt;Microservices&lt;/em&gt;, as you'd call it. &lt;/p&gt;




&lt;h2&gt;
  
  
  Webhooks
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Over 1 million requests every hour&lt;/strong&gt;&lt;br&gt;
It all started years back, during a moment of celebration. We reached the impressive number of &lt;strong&gt;1 million requests an hour&lt;/strong&gt;. A moment worth cheering, yet also a moment in which we discovered something remarkable. We analysed the distribution of these requests, and concluded that over 60% of these requests were &lt;em&gt;webhooks&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Webhooks&lt;/strong&gt;&lt;br&gt;
At Check, users can rent different types of vehicles in our app. During the time of this project, we had integrated the mopeds &lt;a href="https://nz.e-scooter.co/niu-n1s/" rel="noopener noreferrer"&gt;NIU N1S&lt;/a&gt; and &lt;a href="https://shop.segway.com/nl_nl/segway-escooter-e110s.html" rel="noopener noreferrer"&gt;Segway E110S&lt;/a&gt;, as well as the kickscooter &lt;a href="https://www.segway.com/ninebot-kickscooter-max/" rel="noopener noreferrer"&gt;Segway Ninebot MAX&lt;/a&gt;. These providers have both developed APIs for executing commands on their vehicles (turning it on and off) and receiving information about their vehicles (location, mileage, battery percentage). Our backend exposed an API route, that these providers used to POST vehicle's information to, in the form of a webhook. &lt;/p&gt;

&lt;p&gt;For processing a moped's location, this API route processed it as follows;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Receive updates (Eg: &lt;em&gt;moped [x] is now at [coordinates]&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Store raw location and time in the database&lt;/li&gt;
&lt;li&gt;Update the corresponding vehicle's location in the database&lt;/li&gt;
&lt;li&gt;Send a successful response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even though this request was set up as small as possible, it still took our backend around &lt;strong&gt;250ms&lt;/strong&gt; to process these requests. We had around 5000 vehicles back then, so with each vehicle sending us an update every 5 seconds when turned on, it took our backend quite some time to process these updates, all while having to process app users requests as well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third-party bombs&lt;/strong&gt;&lt;br&gt;
Our platform heavily depends on this integration for processing a provider's constant stream of vehicle updates. Even though this integration worked flawlessly most of the time, every once in a while one of the providers would have a small hiccup on their side. These hiccups not only resulted in not receiving their vehicle's updates for a few minutes, but it also meant that we were about to receive something that we internally referred to as 'a bomb' -&amp;gt; a big batch of vehicle updates containing everything that happened during one of these hiccups. In short: &lt;strong&gt;we would sometimes get half an hour of vehicle updates worth within a few seconds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Depending on how big they were, these 'bombs' were notorious for causing instability within our platform. Our backend was unable to process both the user traffic and all these vehicle updates at the same time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Off to better things&lt;/strong&gt;&lt;br&gt;
Longing for a situation where user and fleet update traffic would no longer be processed by the same service, and given that over 60% of incoming traffic during peak hours were webhooks, we decided that this would be the perfect chance for putting our new Kubernetes infrastructure to the test, and so we started building our first microservice.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rust
&lt;/h2&gt;

&lt;p&gt;Due to the sheer volume and relative simplicity of these webhook requests, a clear input (webhook) and output (200 OK status-code), we decided to build a proof of concept using Rust. Rust is a low-level language, primarily known for being strongly type, memory safe and offering great performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The stack&lt;/strong&gt;&lt;br&gt;
The proof-of-concept was built using the following Cargo crates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;rocket&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;serde&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tokio&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;postgres&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;redis&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project compiles into two separate binaries, one for the API service, and one for the consumer service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fleet webhook API service&lt;/strong&gt;&lt;br&gt;
The first component of our Rust microservice is the &lt;em&gt;'Fleet webhook API'&lt;/em&gt;. This service exposes a &lt;a href="https://rocket.rs/" rel="noopener noreferrer"&gt;rocket&lt;/a&gt; API layer with an endpoint for each provider to send their vehicle updates to. &lt;/p&gt;

&lt;p&gt;Once this service receives a webhook, it inserts the raw body to a &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis&lt;/a&gt; queue and immediately responds with a '200 OK'. By not having to read or write to the database during this request, we were able to shave off more than 10x the response time. These little requests now only take a max of &lt;strong&gt;25ms&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fleet consumer service&lt;/strong&gt;&lt;br&gt;
The second component of our Rust microservice is the '&lt;em&gt;Fleet consumer&lt;/em&gt;'. This binary is connected to the same Redis queue, and is responsible for actually processing the updates.&lt;/p&gt;

&lt;p&gt;It updates the moped in the application's database and stores a raw entry of it to a &lt;a href="https://www.timescale.com/" rel="noopener noreferrer"&gt;TimeScale&lt;/a&gt; database (a PostgreSQL database specifically designed to handle large sets of event data).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Separate binaries&lt;/strong&gt;&lt;br&gt;
The great thing about this set up is that we're able to independently scale both of these components. Because the consumers that process the updates are doing most of the heavy lifting, we usually run around three times as many Kubernetes Pods of them, as opposed to the webhook API.&lt;/p&gt;




&lt;h2&gt;
  
  
  New situation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Dealing with bombs&lt;/strong&gt;&lt;br&gt;
This now means that user traffic, as well as back-office traffic, is handled independently from fleet update traffic. When a third party has a hiccup, resulting in loads of fleet updates to process at once, our users will not experience any latency in their apps because even though the microservice will be busy processing these updates, the main API is still sailing smoothly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extensibility&lt;/strong&gt;&lt;br&gt;
This microservice was built prior to when Check released e-cars on its platform. However, when integrating e-cars into the platform using &lt;a href="https://invers.com/en/solutions/cloudboxx/" rel="noopener noreferrer"&gt;Invers' Cloudboxx&lt;/a&gt;, we were able to swiftly implement their AMQP functionality to process live information about our cars, proving the extensibility of the service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Independent scaling&lt;/strong&gt;&lt;br&gt;
We're able to scale our main API independently from this fleet update microservice. With 60% of our traffic being fleet updates, we were able to significantly downscale our main API. Additionally, Rust's focus on performance and minimal resource usage allowed us to reduce costs in the meantime.&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%2Ff8kfrfeqkv4tlse5739s.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%2Ff8kfrfeqkv4tlse5739s.png" alt="Request distribution impact" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;At Check Technologies, our engineering efforts go beyond just adding new features for our users; we actively strive to enhance efficiency, scalability and resilience of the platform. By transitioning to Kubernetes and developing our first microservice in Rust, we were able to overcome the challenges associated with a high volume of webhook requests, ensuring a smooth experience for our users.&lt;/p&gt;

&lt;p&gt;The adoption of a microservices architecture, in combination with our first Rust-based solution, has revolutionised the way we process fleet updates. The 'Fleet webhook API' and 'Fleet consumer services', operating as independent components, enable independent scaling, reducing latency and enhancing overall system stability. We have effectively mitigated the impact of 'third-party bombs,' allowing our main API to sail smoothly even during peak traffic hours.&lt;/p&gt;

&lt;p&gt;As we look back on the progress achieved in our technology stack, we are enthusiastic about the opportunities that lie ahead. At Check Technologies, we are dedicated to raising the bar, finding innovative solutions, and ensuring that our platform continues to be at the forefront of shared mobility technology. The journey has been challenging, but the success of our fleet microservice marks a high note, laying the foundation for sustained growth and ongoing technological advancements in the field of shared mobility.&lt;/p&gt;

</description>
      <category>sharedmobility</category>
      <category>rust</category>
      <category>kubernetes</category>
      <category>microservices</category>
    </item>
  </channel>
</rss>
