<?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: FluxNinja</title>
    <description>The latest articles on DEV Community by FluxNinja (@fluxninjahq).</description>
    <link>https://dev.to/fluxninjahq</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%2Forganization%2Fprofile_image%2F7169%2F5dc42938-fd15-4ce6-8900-6e1d631d3aa7.png</url>
      <title>DEV Community: FluxNinja</title>
      <link>https://dev.to/fluxninjahq</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fluxninjahq"/>
    <language>en</language>
    <item>
      <title>FluxNinja Aperture v1.0 - Managed rate-limiting service, batteries included</title>
      <dc:creator>gitcommitshow</dc:creator>
      <pubDate>Thu, 08 Feb 2024 05:50:09 +0000</pubDate>
      <link>https://dev.to/fluxninjahq/fluxninja-aperture-v10-managed-rate-limiting-service-batteries-included-1405</link>
      <guid>https://dev.to/fluxninjahq/fluxninja-aperture-v10-managed-rate-limiting-service-batteries-included-1405</guid>
      <description>&lt;p&gt;The FluxNinja team is excited to launch “rate-limiting as a service” for developers. This is a start of a new category of essential developer tools to serve the needs of the AI-first world, which relies heavily on effective and fair usage of programmable web resources.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Try out &lt;a href="https://fluxninja.com/" rel="noopener noreferrer"&gt;FluxNinja Aperture&lt;/a&gt; for rate limiting. Join our &lt;a href="https://discord.gg/U3N3fCZEPm" rel="noopener noreferrer"&gt;community on Discord&lt;/a&gt;, appreciate your feedback.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;FluxNinja is leading this new category of “managed rate-limiting service” with the first of its kind, reliable, and battle-tested product. After its first release in 2022, FluxNinja has gone through multiple iterations based on the&lt;br&gt;
feedback from the open source community and paid customers. We are excited to bring the stable version 1.0 of the service to the public.&lt;/p&gt;

&lt;h2&gt;
  
  
  The world needs a managed rate-limiting service
&lt;/h2&gt;

&lt;p&gt;Whether you are self-hosting a service or using a managed-service, balancing the cost and performance remains a challenge. When hosting on your own, you are responsible for scaling to keep up with demand while keeping costs under control. When using a managed service, you have to comply with their request quotas while keeping usage and costs under control.&lt;/p&gt;

&lt;p&gt;This is especially true for applications that use Large Language Models (LLMs). If using cloud-based LLMs, you have to comply with their rate-limits. If using self-hosted LLMs, you have to manage the infrastructure and ensure fair usage. And given the high cost of LLMs, and the shortage of resources such as GPUs, it is crucial to ensure fair usage and cost-efficiency.&lt;/p&gt;

&lt;p&gt;To ensure fair usage and deliver a good user experience while being profitable, developers need to code and manage rate limiting and caching infrastructure. It requires significant engineering efforts and expertise.&lt;/p&gt;

&lt;p&gt;FluxNinja Aperture solves this challenge of building and managing production-grade rate-limiting by providing a managed-rate-limiting service to enforce and comply with rate-limits based on various criteria such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limits based on no. of requests per second&lt;/li&gt;
&lt;li&gt;Per-user limits based on consumed tokens&lt;/li&gt;
&lt;li&gt;Limits based on subscription plans&lt;/li&gt;
&lt;li&gt;Limits based on token-bucket algorithm&lt;/li&gt;
&lt;li&gt;Limits based on concurrency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FluxNinja utilizes a unique approach by separating rate-limiting infrastructure from the core application, which developers don’t need to code or manage anymore. They only need to integrate Aperture SDK, and then rate limiting policies can be updated via UI or API.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We aim to bring production-grade rate-limiting to every app&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Overview of FluxNinja Aperture
&lt;/h2&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%2Fblog.fluxninja.com%2Fassets%2Fimages%2Farchitecture_1_dark-363d8b08ad52ae4729ba3924dd213c25.svg%23gh-dark-mode-only" 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%2Fblog.fluxninja.com%2Fassets%2Fimages%2Farchitecture_1_dark-363d8b08ad52ae4729ba3924dd213c25.svg%23gh-dark-mode-only" alt="Architecture - FluxNinja Aperture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With FluxNinja Aperture, application developers can enforce rate-limits on the usage of their services or comply with rate-limits of various external services. This ensures reliability of your services, fair usage and cost control.&lt;/p&gt;

&lt;p&gt;FluxNinja Aperture provides a managed rate-limiting service that handles the complexities behind the scenes, requiring only simple SDK integration in your application.&lt;/p&gt;

&lt;p&gt;These are the key features of FluxNinja Aperture rate-limiting service:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rate &amp;amp; Concurrency Limiting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Optimize cost and ensure fair access by implementing fine-grained rate-limits. Regulate the use of expensive pay-as-you-go APIs such as OpenAI and reduce the load on self-hosted models such as Mistral.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caching&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cache LLM results and reuse them for similar requests to reduce cost and boost performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request Prioritization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Manage utilization of constrained LLM resources at the level of each request by prioritizing paid over free tier users and interactive over background queries. Ensure fair access across users during peak usage hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workload observability&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Get unprecedented visibility into your workloads with detailed traffic analytics on request rates, tokens, and latencies sliced by features, users, request types, and any other arbitrary business attribute.&lt;/p&gt;

&lt;p&gt;For more info, check out &lt;a href="https://docs.fluxninja.com/" rel="noopener noreferrer"&gt;FluxNinja Aperture Docs&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges with traditional rate-limiting solutions
&lt;/h2&gt;

&lt;p&gt;Traditional approaches to rate-limiting, typically involving custom-built solutions with in-memory data stores such as Redis, have presented significant&lt;br&gt;
challenges.&lt;/p&gt;

&lt;p&gt;Managing the codebase and infrastructure for rate-limiting demands regular attention from engineers and DevOps, incurring significant costs.&lt;/p&gt;

&lt;p&gt;API gateways work for limited use cases; they lack the context-specific understanding required for business aware rate-limiting (e.g., per-user limits or subscription-based restrictions).&lt;/p&gt;

&lt;p&gt;There is currently no ready-made solution where a distributed application needs to comply with rate-limits of an external service.&lt;/p&gt;

&lt;p&gt;These limitations highlight the need for a more efficient, context-aware, and easy-to-manage rate-limiting solution suitable for modern application demands.&lt;/p&gt;

&lt;h2&gt;
  
  
  How FluxNinja Aperture solves these gaps
&lt;/h2&gt;

&lt;p&gt;Aperture separates rate-limiting infrastructure from the application code. You can self-host it using the Aperture open source package or use the hosted solution - Aperture Cloud. To manage rate-limits, you only need to integrate Aperture SDKs in your programming language.&lt;/p&gt;

&lt;p&gt;Benefits compared to custom Redis-based or makeshift solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No need to code and manage complex rate-limiting algorithms and infrastructure&lt;/li&gt;
&lt;li&gt;Rate-limit policies and algorithms are updated centrally via UI or API rather than application code changes&lt;/li&gt;
&lt;li&gt;Real-time analytics dashboards to monitor and tune configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;With FluxNinja Aperture, the heavy lifting is offloaded, allowing you to focus&lt;br&gt;
on business logic while still retaining control over policies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;FluxNinja Aperture also integrates with existing service mesh and API gateways, giving a quick upgrade to your existing rate-limiting infrastructure.&lt;/p&gt;

&lt;p&gt;You can easily configure these constraints using Aperture policies. And then wrap your code block with Aperture SDK calls where you use these external or internal services. Using the Aperture Cloud UI, you’ll be able to monitor the&lt;br&gt;
workload and effectiveness of rate-limit policies.&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%2Fblog.fluxninja.com%2Fassets%2Fimages%2Fmonitoring-5b68575641e3007f078fb3a8ac4c1624.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%2Fblog.fluxninja.com%2Fassets%2Fimages%2Fmonitoring-5b68575641e3007f078fb3a8ac4c1624.png" alt="Screenshot - Monitoring Feature"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://docs.fluxninja.com/get-started/" rel="noopener noreferrer"&gt;this example&lt;/a&gt; to get started with enforcing or complying with rate-limits using FluxNinja Aperture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customer case study
&lt;/h2&gt;

&lt;p&gt;CodeRabbit is a leading AI Code Review tool and they are an early adopter of FluxNinja Aperture. The CodeRabbit app consumes several LLM APIs. They offer&lt;br&gt;
code review services through various subscription tiers to their users, including a free trial and an unlimited plan for open source projects. The high cost of LLM services and huge demand for their own service made it a challenge&lt;br&gt;
to offer an accessible pricing for their users while being cost-efficient. CodeRabbit uses FluxNinja Aperture to prioritize, cache, and rate-limit requests&lt;br&gt;
based on user tier preference and time criticality. FluxNinja helps them &lt;a href="https://blog.coderabbit.ai/blog/how-we-built-cost-effective-generative-ai-application" rel="noopener noreferrer"&gt;deliver a great user experience while being cost-efficient&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion (tl;dr;)
&lt;/h2&gt;

&lt;p&gt;Rate limiting is crucial for web services, especially for those using Generative AI, to ensure fair usage, cost-efficiency, and a better user experience. Traditional methods often require heavy engineering work and struggle to address&lt;br&gt;
more nuanced needs such as user-specific or token-based limits.&lt;/p&gt;

&lt;p&gt;FluxNinja Aperture solves this by providing an SDK-driven managed-rate-limiting service, making it easy to enforce your own rate-limits and comply with rate limits of the services you use. With FluxNinja Aperture, teams do not need to&lt;br&gt;
invest their engineering bandwidth in building and maintaining complex rate limiting infrastructure. You can self-host FluxNinja Aperture on your premise or&lt;br&gt;
use the cloud offering at a nominal cost. It is as easy as integrating the FluxNinja SDK in your Node.js, Python, Golang, or Java backend apps.&lt;/p&gt;

&lt;p&gt;FluxNinja team is excited to unveil this tool publicly for developers. Join us in this journey of bringing production-grade rate-limiting to every app.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Visit &lt;a href="https://docs.fluxninja.com/" rel="noopener noreferrer"&gt;FluxNinja Aperture docs&lt;/a&gt; to get started with enforcing or complying with rate limits now.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>launch</category>
      <category>ratelimiting</category>
      <category>generativeai</category>
      <category>aiops</category>
    </item>
    <item>
      <title>Prototype to Production Roadmap for Generative AI-based Products</title>
      <dc:creator>gitcommitshow</dc:creator>
      <pubDate>Tue, 06 Feb 2024 18:30:00 +0000</pubDate>
      <link>https://dev.to/fluxninjahq/prototype-to-production-roadmap-for-generative-ai-based-products-1idh</link>
      <guid>https://dev.to/fluxninjahq/prototype-to-production-roadmap-for-generative-ai-based-products-1idh</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;As we enter 2024, Generative AI-based applications are poised to become&lt;br&gt;
mainstream&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Given Generative AI’s limitations at the start of 2023, the world was skeptical whether Generative AI would deliver tangible value to the businesses and to the customers. With the current state of Generative AI services, it seems totally possible. Many of us have by now built some prototypes of Generative AI-based apps that are effectively solving specific business problems and delivering concrete value to a small set of users.&lt;/p&gt;

&lt;p&gt;This was possible due to continuous improvements in Generative AI services from GPT-3.5 to GPT-4-Turbo, from LlaMa to Mistral, and many more incremental as well as disruptive developments. We were able to confidently use Generative AI services to deliver value consistently, and the dream of building useful Generative AI-based apps is not a dream anymore but a reality.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In 2024, we will see massive adoption of such Generative AI-based products&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After building a prototype, the next challenge one needs to solve is how to ship those prototypes to the hands of millions of such users reliably in production. And that is not yet done by many but has been proven to be possible.&lt;/p&gt;

&lt;p&gt;A prime example of this is &lt;a href="https://coderabbit.ai/"&gt;CodeRabbit&lt;/a&gt;, a leading AI Code Review tool that utilizes GPT for &lt;strong&gt;automating PR reviews&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nwoDxdOK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.fluxninja.com/assets/images/coderabbit-gpt-usage-58a219990c4174df8cac51e07abd031c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nwoDxdOK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.fluxninja.com/assets/images/coderabbit-gpt-usage-58a219990c4174df8cac51e07abd031c.png" alt="CodeRabbit’s monthly GPT API usage for the code review use case" width="500" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CodeRabbit was launched in Sep 2023, and has already scaled to 1 million+ monthly requests served using GPT APIs. Its 100% success in delivering those requests and a satisfied customer base of 57000+ code repositories demonstrates the practical viability of Generative AI in building scalable businesses that deliver concrete value to users.&lt;/p&gt;

&lt;p&gt;Transitioning from a prototype to a production stage is not as easy though, it involves several challenges. These include managing operational costs, preventing service abuse, handling AI service outages, and maintaining a robust user experience while scaling to accommodate millions of users. CodeRabbit's journey exemplifies that with the right approach, these challenges can be overcome to achieve success in production.&lt;/p&gt;

&lt;p&gt;This article aims to guide you through the process of transitioning your Generative AI-based application from prototype to production. We will discuss strategies to address the common hurdles such as cost efficiency, reliability, scalability, and user experience optimization. The goal of this article is to provide a clear, technical roadmap for scaling your Generative AI application effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding foundational Generative AI models and services
&lt;/h2&gt;

&lt;p&gt;There are multiple foundational Generative AI models and services encompassing a wide range of technologies that have the capability to generate new content, solve problems, or process information in innovative ways. These services can be&lt;br&gt;
utilized to enable more specific use cases.&lt;/p&gt;

&lt;p&gt;These Generative AI models/services can be broadly categorized as follows:&lt;/p&gt;

&lt;h4&gt;
  
  
  Text generation and processing
&lt;/h4&gt;

&lt;p&gt;Leading models to generate or process text are - &lt;a href="https://openai.com/gpt-4"&gt;GPT-4&lt;/a&gt;, &lt;a href="https://mistral.ai/"&gt;Mistral&lt;/a&gt;, &lt;a href="https://www.anthropic.com/index/introducing-claude"&gt;Claude&lt;/a&gt;, &lt;a href="https://ai.meta.com/llama/"&gt;LlaMa&lt;/a&gt;, and so on. They can generate human-like text, answer questions, summarize content, translate languages, and more. Most of these models are also available as API service, so it is easier to implement those without worrying about the model deployment. But you have to do all other things related to productizing the solutions built on top of these services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automated Writing Assistants for Grammar checking, style improvement, and content generation, and so on.(such as Grammarly or ProWritingAid)&lt;/li&gt;
&lt;li&gt;Automatically generate draft blog posts and articles&lt;/li&gt;
&lt;li&gt;Create conversational dialogue for chatbots and virtual assistants&lt;/li&gt;
&lt;li&gt;Generate ideas and creative story premises for writers&lt;/li&gt;
&lt;li&gt;Summarize texts and documents for consumers&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Image generation
&lt;/h4&gt;

&lt;p&gt;Image Generation models such as &lt;a href="https://www.midjourney.com/"&gt;MidJourney&lt;/a&gt;, &lt;a href="https://stability.ai/"&gt;Stable Diffusion&lt;/a&gt;, &lt;a href="https://openai.com/dall-e-2"&gt;DALL-E&lt;/a&gt;, &lt;a href="https://imagen.research.google"&gt;Imagen&lt;/a&gt;, &lt;a href="https://imagen.research.google/editor/"&gt;Imagen Editor&lt;/a&gt; etc. can create images and artworks from textual descriptions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate unique profile pictures and avatars&lt;/li&gt;
&lt;li&gt;Create original artwork for digital artists and designers&lt;/li&gt;
&lt;li&gt;Produce images for marketing materials and social media posts&lt;/li&gt;
&lt;li&gt;Conceptualize product designs through visualizations&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Voice generation
&lt;/h4&gt;

&lt;p&gt;There are multiple high-quality Text-to-Speech (TTS) models and services available now that convert text into spoken voice, such as&lt;br&gt;
&lt;a href="https://elevenlabs.io/"&gt;ElevanLabs&lt;/a&gt;, &lt;a href="https://coqui.ai/"&gt;XTTS/Coqui&lt;/a&gt;, &lt;a href="https://deepmind.google/discover/blog/wavenet-a-generative-model-for-raw-audio/"&gt;WaveNet&lt;/a&gt;, &lt;a href="https://aws.amazon.com/polly/"&gt;Amazon Polly&lt;/a&gt;, etc.&lt;/p&gt;

&lt;h4&gt;
  
  
  Music or Sound generation
&lt;/h4&gt;

&lt;p&gt;Some of the popular music or sound generation models/service include &lt;a href="https://audiocraft.metademolab.com/musicgen.html"&gt;MusicGen&lt;/a&gt;, &lt;a href="https://audiocraft.metademolab.com/audiogen.html"&gt;AudioGen&lt;/a&gt;, &lt;a href="https://about.fb.com/news/2023/08/audiocraft-generative-ai-for-music-and-audio/"&gt;AudioCraft&lt;/a&gt;, &lt;a href="https://openai.com/research/jukebox"&gt;Jukebox&lt;/a&gt;, &lt;a href="https://magenta.tensorflow.org/"&gt;Magenta&lt;/a&gt;, &lt;a href="https://deepmind.google/discover/blog/wavenet-a-generative-model-for-raw-audio/"&gt;WaveNet&lt;/a&gt;, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compose background music for videos and other multimedia&lt;/li&gt;
&lt;li&gt;Create custom ringtones and notification sounds&lt;/li&gt;
&lt;li&gt;Produce sound effects for games, VR, and AR experiences&lt;/li&gt;
&lt;li&gt;Generate musical ideas and samples for musicians&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Video generation
&lt;/h4&gt;

&lt;p&gt;This category is not as mature as other Generative AI categories and we might have to wait a bit more for improvements to see massive amounts of practical use&lt;br&gt;
cases. Presently, some of the popular models/services to generate video from text/image are - &lt;a href="https://runwayml.com/"&gt;RunwayML&lt;/a&gt;, &lt;a href="https://replicate.com/nightmareai/cogvideo"&gt;CogVideo&lt;/a&gt;, &lt;a href="https://imagen.research.google/video/"&gt;Imagen&lt;/a&gt;, &lt;a href="https://makeavideo.studio/"&gt;Make-a-Video&lt;/a&gt;, &lt;a href="https://phenaki.video/"&gt;Phenaki&lt;/a&gt;, &lt;a href="https://www.synthesia.io/"&gt;Synthesia&lt;/a&gt;, &lt;a href="https://stability.ai/stable-video"&gt;Stable Video&lt;/a&gt;, &lt;a href="https://blog.research.google/2023/12/videopoet-large-language-model-for-zero.html"&gt;VideoPoet&lt;/a&gt;,&lt;br&gt;
and so on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatically produce training videos for educational purposes&lt;/li&gt;
&lt;li&gt;Create visual marketing content to promote brands and offerings&lt;/li&gt;
&lt;li&gt;Generate video templates and effects for editing&lt;/li&gt;
&lt;li&gt;Conceptualize scene frameworks for filmmakers and creators&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  General purpose
&lt;/h4&gt;

&lt;p&gt;Reinforcement learning models - can be optimized to complete various sequential decision-making tasks like game playing, autonomous robotics vehicle operations.&lt;br&gt;
Some examples in this category are - &lt;a href="https://www.nvidia.com/en-us/self-driving-cars/"&gt;NVIDIA DRIVE&lt;/a&gt; (self-driving solutions), &lt;a href="https://deepmind.google/discover/blog/alphazero-shedding-new-light-on-chess-shogi-and-go/"&gt;AlphaZero&lt;/a&gt; (chess game playing).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Play games against humans by mastering gameplay strategy&lt;/li&gt;
&lt;li&gt;Control robotic systems to automate business processes&lt;/li&gt;
&lt;li&gt;Optimize machine behaviors for complex sequential tasks&lt;/li&gt;
&lt;li&gt;Develop product innovations through iterative simulated testing&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Future categories
&lt;/h4&gt;

&lt;p&gt;In 2024, we might see some more categories as more foundational models are created that are optimized for a specific task or industry and enabling more use&lt;br&gt;
cases. Some of the new categories we predict to get developed in 2024 will be related to data analysis, predictions, gaming, industrial automation, autonomous&lt;br&gt;
vehicles, healthcare diagnostic, adaptive learning, and explainable AI (XAI).&lt;/p&gt;

&lt;p&gt;You or your competitors might have built a prototype based on these foundational models or services already. If not, it is likely that you’ll do that in 2024.&lt;br&gt;
However, how do you move beyond the prototype and make it available in production to real users, and that too at a practical scale which drives significant impact?&lt;/p&gt;

&lt;h2&gt;
  
  
  Path from prototype to production
&lt;/h2&gt;

&lt;p&gt;Drawing on my personal experience and the insights gained from others, let me share the specific steps to take your Generative AI-based solution from prototype to production. I'll also provide tips for each step to take specific actions in your journey to productionize your Generative AI-based application.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Choose the right Generative AI model or service for the task
&lt;/h3&gt;

&lt;p&gt;Start with the basic understanding of various models that you can use and how they fit your requirements. The earlier section might have provided you a high-level overview. To move to the next step of choosing the right model or&lt;br&gt;
service, explore some of the popular comparison/benchmarks for Generative AI models such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://huggingface.co/spaces/lmsys/chatbot-arena-leaderboard"&gt;Chatbot Arena Leaderboard&lt;/a&gt; provides comparison of various LLMs based on various benchmarks. Their &lt;a href="https://arena.lmsys.org/"&gt;official website&lt;/a&gt; provides more utilities for comparison.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard"&gt;Open LLM Leaderboard&lt;/a&gt; provides comparison of Open Source LLMs based on various benchmarks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While these general benchmarks can help with high level filtering of the models/services you might want to use, one must test these models against their full application requirements using a large enough sample to prove production&lt;br&gt;
readiness. Assess accuracy, relevance, runtime, and other performance metrics for your use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Manage prompt engineering effectively
&lt;/h3&gt;

&lt;p&gt;To effectively use a Generative AI model/service, you need to provide and iterate on prompts. Your service quality depends on it. Which is why managing your prompts for the AI services is crucial in production. Following techniques&lt;br&gt;
can help&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Curate a library of tested base prompts - Start by gathering prompts used during prototyping that yield high quality, relevant outputs in your domain. These can serve as standard building blocks.&lt;/li&gt;
&lt;li&gt;Log all prompts and iterations - Track all prompts and model versions in your production systems, along with key metric scores. Analyze for continuous refinement.&lt;/li&gt;
&lt;li&gt;Implement prompt templating conventions - Structure prompts into clear components like task framing, content constraints, tone/style parameters, etc. to simplify iteration.&lt;/li&gt;
&lt;li&gt;Build a prompt enrichment pipeline - Augment prompts with external data like lexicons, knowledge bases, and human feedback to improve them over time.&lt;/li&gt;
&lt;li&gt;Control variations with conditional parameters - For user personalization or experimentation, rely more on conditional tuning of style, length, etc. rather than fully custom prompts.&lt;/li&gt;
&lt;li&gt;Allow spaces for innovation - Leave room within composable prompt templates to keep introducing and testing new creative variants.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Monitor quality and mitigate hallucinations
&lt;/h3&gt;

&lt;p&gt;Once you see that your solution is working on your local or for some users, you must not stop there, you still need to think about quality control via monitoring and specifically to manage hallucinations. Some of the following&lt;br&gt;
techniques can help.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automate testing process to identify issues early - You can &lt;strong&gt;use LLM as an evaluation tool&lt;/strong&gt; to automate some of the tasks in the testing process. Run those tests before releasing the new version to customers.&lt;/li&gt;
&lt;li&gt;Set up monitoring for model drift - As data patterns/distributions shift over time, monitor drops in prompt effectiveness and update appropriately.&lt;/li&gt;
&lt;li&gt;Check outputs for consistency - Spot check generations directly for coherence, factuality, toxicity to catch model performance regressions requiring prompt tuning.&lt;/li&gt;
&lt;li&gt;Controlled beta access - We recommend releasing controlled beta access to test the product quality and identify hallucinations. It ensures the application hits quality requirements, establishes a clear user agreement, and protects key reputation aspects during closed beta with selective users.&lt;/li&gt;
&lt;li&gt;Human review as the last defending line - You should also have a human review step for some of the different test cases before deploying the release to customers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Wrap as a production API/service
&lt;/h3&gt;

&lt;p&gt;Expose core functionality via REST or other APIs for easy integration into the application front-end. Add input validation, authentication, monitoring, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Establish scalable infrastructure
&lt;/h3&gt;

&lt;p&gt;Standard generative models have significant system resource demands. Even when you’re using the services which are taking care of those scalable infrastructure needs (e.g. GPT API), the number of requests you’ll be sending to those services&lt;br&gt;
will be quite high and that will require you to think about your infrastructure.&lt;br&gt;
Assess expected request loads and build a distributed cloud infrastructure for cost-efficient scalability. You will likely need to containerize using&lt;br&gt;
Docker/Kubernetes and set up auto-scaling.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Setup rate limiting for cost optimization and service abuse protection
&lt;/h3&gt;

&lt;p&gt;When it comes to load management, the requirements for Generative AI-based applications are way higher than your usual systems. The amount of service load is humongous as your system is talking to an external Generative AI service at a frequency which your normal app usually wouldn't need. You and your customers will frequently find dealing with errors such as “429 - Too many requests” and CPU usage going close to 100%. Avoiding these issues and the requirement of low latency is critical to retain your customers.&lt;/p&gt;

&lt;p&gt;It is not easy, and making your application accessible from a service availability or cost perspective is fundamental to grow adoption for your product. If you use the right tools, rate limiting and caching can be easy pickings in ensuring your service does not go out of service or becomes&lt;br&gt;
unsustainably expensive to manage.&lt;/p&gt;

&lt;p&gt;Tools such as &lt;a href="https://fluxninja.com"&gt;FluxNinja Aperture&lt;/a&gt; can be helpful, which are purposefully designed to protect Generative AI-based applications from abuse&lt;br&gt;
and high cost.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lhHExiip--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.fluxninja.com/assets/images/aperture-architecture-363d8b08ad52ae4729ba3924dd213c25.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lhHExiip--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.fluxninja.com/assets/images/aperture-architecture-363d8b08ad52ae4729ba3924dd213c25.svg" alt="FluxNinja Aperture architecture" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As shown in the above architecture diagram, FluxNinja Aperture or your own custom solution for rate limiting needs to sit in between your app’s backend and&lt;br&gt;
Generative AI service to takes care of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rate limiting request based on the quota for your Generative AI service&lt;/li&gt;
&lt;li&gt;Queuing requests to avoid overwhelming your system or the external Generative AI service system you use&lt;/li&gt;
&lt;li&gt;Prioritizing requests based on user tiers&lt;/li&gt;
&lt;li&gt;Caching requests to reduce external AI service costs and deliver results to user faster&lt;/li&gt;
&lt;li&gt;Monitor your service health and how you’re interacting with the external AI service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using FluxNinja, all these steps can be done with a simple sdk integration of FluxNinja in your code. It can provide you protection from abuse as well as control your costs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--swuWaJ1P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.fluxninja.com/assets/images/api-producer-vs-consumer-needs-ea3968e22e9e33bcc1203aca559594fe.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--swuWaJ1P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.fluxninja.com/assets/images/api-producer-vs-consumer-needs-ea3968e22e9e33bcc1203aca559594fe.jpg" alt="Two key needs to improve user experience for Generative AI app" width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the end, this step will result in a better user experience and boost your app’s growth.&lt;/p&gt;

&lt;p&gt;Treat these 6 steps as a checklist for going from prototype to production.&lt;br&gt;
Appreciate your thoughts and tips to make this checklist even better.&lt;/p&gt;

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

&lt;p&gt;The Generative AI-based application market is growing exponentially. There is an opportunity to use this to grow and stay ahead of your competition. Although it is not an easy task to take your Generative AI-based product from prototype to production, it is totally possible to achieve it, similar to &lt;a href="https://blog.fluxninja.com/blog/coderabbit-cost-effective-generative-ai"&gt;how CodeRabbit built a cost-effective Generative AI-based app&lt;/a&gt;. And you can make it easier by approaching in the organized manner and utilizing the right tools, as we discussed in this article. Some of the key points to remember are - choose the appropriate model, manage prompts effectively, use testing techniques specific to Generative AI-based apps, implement rate limiting and caching.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aiops</category>
      <category>ratelimiting</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Balancing Cost and Efficiency in Mistral with Concurrency Scheduling</title>
      <dc:creator>Karanbir Sohi</dc:creator>
      <pubDate>Thu, 25 Jan 2024 09:28:05 +0000</pubDate>
      <link>https://dev.to/fluxninjahq/balancing-cost-and-efficiency-in-mistral-with-concurrency-scheduling-524f</link>
      <guid>https://dev.to/fluxninjahq/balancing-cost-and-efficiency-in-mistral-with-concurrency-scheduling-524f</guid>
      <description>&lt;p&gt;In the fast-evolving space of generative AI, OpenAI's models are the go-to choice for most companies for building AI-driven applications. But that may change soon as open-source models catch up by offering much better economics and data privacy through self-hosted models. One of the notable competitors in this sector is &lt;a href="https://mistral.ai"&gt;Mistral AI&lt;/a&gt;, a French startup, known for its innovative and lightweight models, such as the &lt;a href="https://mistral.ai/news/announcing-mistral-7b/"&gt;open-source Mistral 7B&lt;/a&gt;.&lt;br&gt;
Mistral has gained attention in the industry, particularly because their model is free to use and can be self-hosted. However, generative AI workloads are computationally expensive, and due to the limited supply of Graphics Processing Units (GPUs), scaling them up quickly is a complex challenge. Given the insatiable hunger for LLM APIs within organizations, there is a potential imbalance between demand and supply. One possible solution is to prioritize access to LLM APIs based on request criticality while ensuring fair access among users during peak usage. At the same time, it is important to ensure that the provisioned GPU infrastructure gets maximum utilization.&lt;/p&gt;

&lt;p&gt;In this blog post, we will discuss how FluxNinja Aperture's Concurrency Scheduling and Request Prioritization features significantly reduce latency and ensure fairness, at no added cost, when executing generative AI workloads using the Mistral 7B Model. By improving performance and user experience, this integration is a game-changer for developers focusing on building cutting-edge AI applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistral 7B: The Open Source LLM from French Startup Mistral AI
&lt;/h2&gt;

&lt;p&gt;This powerful model boasts 7 billion parameters and a sequence length of up to 8k, making it an efficient choice for tackling various tasks.&lt;/p&gt;

&lt;p&gt;In the world of LLMs, Mistral 7B is renowned for its impressive performance, defeating its counterparts Llama 1 and 2 on numerous benchmarks. The open-source nature of this model has paved the way for a multitude of opportunities, enabling startups to offer cost-effective AI applications by running Mistral locally or offering LLMs as a service.&lt;/p&gt;

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

&lt;p&gt;Mistral AI's decision to open-source Mistral 7B is a step towards leveling the playing field for smaller players in the competitive AI landscape. It not only empowers developers and businesses but also fosters collaboration and innovation within the industry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost Problem: Self-Hosted Vs AI APIs Providers
&lt;/h2&gt;

&lt;p&gt;The adoption of AI models like Mistral has become increasingly popular across industries, leading to various challenges associated with their deployment and usage. One such challenge is the &lt;a href="https://www.cnbc.com/2023/03/13/chatgpt-and-generative-ai-are-booming-but-at-a-very-expensive-price.html"&gt;cost of operating these models&lt;/a&gt;, whether through self-hosting or using API endpoints.&lt;/p&gt;

&lt;p&gt;Self-hosted models and API Cloud-hosted commercial models each present distinct advantages and limitations when it comes to using AI models like Mistral. However, as demand for these models continues to surge, companies face growing challenges in maintaining optimal performance and user experience while keeping costs under control.&lt;/p&gt;

&lt;p&gt;Opting for LLMs like OpenAI will relieve your team from operational burdens, but you'll still encounter strict rate limit quotas and the need to manage costs effectively with pay-as-you-go models that scale based on usage. This can be a challenge as usage might not always be predictable. Alternatively, self-hosting LLMs can save costs in the long run, but it requires building in-house expertise for deployment and operation. While you'll no longer have to worry about rate limits, you will need to manage saturated infrastructure that is neither cost-effective nor easy to scale instantly. To ensure priority access across workloads and fairness among users of your service, some form of control is necessary. The high demand for AI resources and the associated costs pose a significant challenge for businesses. Companies must balance the need for cost savings against ensuring uninterrupted access to these essential tools.&lt;/p&gt;

&lt;p&gt;FluxNinja Aperture is a purpose-built solution for streamlining the consumption of LLMs. It offers the ability to implement rate limits to ensure fair access across users for any workload. For self-hosted models, it can enforce concurrency limits and provide fair and prioritized access across workloads and users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Mistral with Concurrent User Access
&lt;/h2&gt;

&lt;p&gt;We used &lt;a href="https://ollama.ai"&gt;Ollama&lt;/a&gt; to quickly install a local Mistral instance on our machine and send prompts to their endpoint. To simulate real-world conditions, we performed the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Compiled a set of 25 diverse prompts for both open-source and paid users, which included coding challenges, sales pitches, legal questions, content generation, etc.&lt;/li&gt;
&lt;li&gt;Developed a Typescript Application to manage both scripts and return responses from the generated answers.&lt;/li&gt;
&lt;li&gt;Implemented a script to run the application concurrently for both user types based on a predefined number of users.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In our initial test run, we operated with only 2 concurrent users (1 open source and 1 paid), thus sending 50 prompts in total. We noticed that Mistral demonstrated impressive effectiveness, delivering answers within the range of 10-30 seconds based on the prompt context.&lt;/p&gt;

&lt;p&gt;For the subsequent test, we increased the number of simultaneous users to 10 (5 per tier), resulting in a total of 250 prompts being processed. Initially, responses were generated swiftly; however, after handling a few queries, we&lt;br&gt;
began experiencing noticeable delays, with wait times escalating up to 5 minutes and beyond.&lt;/p&gt;

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

&lt;p&gt;This realization led us to acknowledge that despite Mistral's user-friendly deployment process, the GPU limitation significantly hinders the performance of concurrent generative AI workloads. In the real world, the number of GPUs will be much higher, but so will the number of concurrent users. Therefore a degradation in user experience and performance issues cannot be neglected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Maximizing Efficiency with FluxNinja Aperture's Concurrency Scheduling Feature
&lt;/h2&gt;

&lt;p&gt;FluxNinja Aperture’s Concurrency Scheduling feature allows practitioners to set the maximum concurrent requests that a system can handle at a given time. Any request exceeding concurrency will get queued by Aperture. Queuing can be done on a priority basis, which can be defined based on the specific business criteria, and passed via the &lt;a href="https://docs.fluxninja.com/sdk/"&gt;Aperture SDK&lt;/a&gt;. By defining priorities, organizations can ensure that crucial or revenue-generating&lt;br&gt;
requests are processed promptly and efficiently. For example, when our application caters to two tiers of users – paid and open source – prioritizing paid requests during periods when Mistral's computation was slowing down significantly enhanced overall business efficiency. Based on the priority,&lt;br&gt;
Aperture will bump up high-priority requests over low-priority ones. This feature is designed to keep the user experience optimal, ensure stable operations, and prevent overloading.&lt;/p&gt;

&lt;p&gt;Let's take a look at how easily the Aperture SDK can be integrated with an existing App.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aperture SDK Integration
&lt;/h3&gt;

&lt;p&gt;FluxNinja Aperture has a ready-to-use &lt;a href="https://docs.fluxninja.com/sdk/javascript/define-feature-control-points-using-javascript-sdk"&gt;TypeScript SDK&lt;/a&gt; that can be integrated and used within minutes.&lt;/p&gt;

&lt;p&gt;After signing up on &lt;a href="https://app.fluxninja.com/sign-up"&gt;Aperture Cloud&lt;/a&gt; to start our 30-day free trial, and installing the &lt;a href="https://www.npmjs.com/package/@fluxninja/aperture-js"&gt;latest version&lt;/a&gt; of the SDK in our repository, let's create an Aperture Client instance, passing the organization endpoint and API key, which can be found by clicking on the Aperture Tab within the Aperture Cloud UI.&lt;/p&gt;

&lt;h4&gt;
  
  
  Integration with Aperture SDK
&lt;/h4&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;ApertureClient&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="s2"&gt;@fluxninja/aperture-js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create aperture client&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apertureClient&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;ApertureClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ORGANIZATION.app.fluxninja.com:443&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step consists of setting up essential business labels to prioritize requests. In our case, requests should be prioritized by user tier classifications:&lt;/p&gt;

&lt;h4&gt;
  
  
  Defining Priorities
&lt;/h4&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;userTiers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;paid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open-source&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following step is making a &lt;code&gt;startFlow&lt;/code&gt; call to Aperture before sending a request to Mistral. For this call, it is important to specify the control point (&lt;code&gt;mistral-prompt&lt;/code&gt; in our example) and the labels that will align with the&lt;br&gt;
concurrency scheduling policy. The priority label is necessary for request prioritization, while the workload label differentiates each request.&lt;/p&gt;

&lt;p&gt;According to the policy logic designed to limit the number of concurrent requests sent to Mistral, Aperture will, on each &lt;code&gt;startFlow&lt;/code&gt; call, either give precedence to a critical request or queue a less urgent one when approaching the concurrency limit. The duration a request remains in the queue is determined by the gRPC deadline, set within the &lt;code&gt;startFlow&lt;/code&gt; call. Setting this deadline to &lt;code&gt;120000&lt;/code&gt; milliseconds, for example, indicates that the request can be queued for a maximum of 2 minutes. After this interval, the request will be rejected.&lt;/p&gt;

&lt;p&gt;Once the &lt;code&gt;startFlow&lt;/code&gt; call is made, we send the prompt to Mistral and wait for its response. Excess requests are automatically queued by Aperture. It is important to make the &lt;code&gt;end&lt;/code&gt; call after processing each request to send telemetry data that would provide granular visibility for each flow.&lt;/p&gt;

&lt;h4&gt;
  
  
  Start &amp;amp; End Flow Functionality
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;flow&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;apertureClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startFlow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mistral-prompt&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;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tier&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; user`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;grpcCallOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;deadline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;120000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// ms&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:11434/api/generate&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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&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;Content-Type&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;application/json&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;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;requestBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error sending prompt to Mistral:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;flow&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;end&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;
  
  
  Aperture Cloud Policy
&lt;/h3&gt;

&lt;p&gt;Now, the final step is to set up a Concurrency Scheduling Policy within the Aperture Cloud UI.&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;code&gt;Policies&lt;/code&gt; tab on the sidebar menu, and select &lt;code&gt;Create Policy&lt;/code&gt; in the upper-right corner. Next, choose the Rate Limiting blueprint, select Concurrency, and complete the form with these specific values:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Policy name&lt;/code&gt;: Unique for each policy, this field can be used to define policies tailored for different use cases. Set the policy name to &lt;code&gt;concurrency-scheduling-test&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Limit by label key&lt;/code&gt;: Determines the specific label key used for concurrency limits. This parameter becomes essential for more granular concurrency limiting use cases such as, per-user limiting where a parameter like the &lt;code&gt;user_id&lt;/code&gt; can be passed. For now, we will test global concurrency limiting, we will leave the label as it is.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Max inflight duration&lt;/code&gt;: Configures the time duration after which flow is assumed to have ended in case the end call gets missed. We'll set it to &lt;code&gt;60s&lt;/code&gt; as an example.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Max concurrency&lt;/code&gt;: Configures the maximum number of concurrent requests that a service can take. We'll set it to &lt;code&gt;2&lt;/code&gt; as an example.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Priority label key&lt;/code&gt;: This field specifies the label that is used to determine the priority. We will leave the label as it is.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Tokens label key&lt;/code&gt;: This field specifies the label that is used to determine tokens. We will leave the label as it is.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Workload label key&lt;/code&gt;: This field specifies the label that is used to determine the workload. We will leave the label as it is.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Control point&lt;/code&gt;: It can be a particular feature or execution block within a service. We'll use &lt;code&gt;mistral-prompt&lt;/code&gt; as an example.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Once you've completed these fields, click &lt;code&gt;Continue&lt;/code&gt; and then &lt;code&gt;Apply Policy&lt;/code&gt; to finalize the policy setup.&lt;/p&gt;

&lt;p&gt;The integration with the Aperture SDK and this policy will make sure that every time a request is sent to Mistral, Aperture will either send it or queue it based on the concurrency and priority labels provided.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring Concurrent Requests Sent to Mistral
&lt;/h2&gt;

&lt;p&gt;We concurrently ran the same script with 5 users for each tier and a total of 250 prompts as mentioned earlier. But this time, we saw quicker response times, particularly for paid users, due to assigning a higher priority.&lt;/p&gt;

&lt;p&gt;Aperture's ability to collect advanced telemetry data allowed us to see how workloads performed, how many requests got queued, and what the latency of processing workloads was.&lt;/p&gt;

&lt;p&gt;Here is the graph that we observed:&lt;/p&gt;

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

&lt;p&gt;Here is the queueing and prioritization of requests when the max concurrency is met, and how paid requests get bumped up in the queue.&lt;/p&gt;

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

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

&lt;p&gt;In this blog post, we delved into the potential of the open-source model Mistral for building AI-driven apps and addressed the challenge of slow responses resulting from GPU limitations when handling multiple concurrent requests. This issue significantly impacts user experience and performance, two critical factors for revenue generation and user acquisition in today's competitive market.&lt;/p&gt;

&lt;p&gt;To address this challenge, we introduced FluxNinja Aperture's Concurrency Scheduling and Request Prioritization features, essential to managing Mistral's workload while ensuring optimal user experience and performance. With FluxNinja Aperture, practitioners can build cost-effective AI-driven applications without incurring excessive infrastructure expenses during demand surges. Simultaneously, they maintain smooth operations.&lt;/p&gt;

&lt;p&gt;By leveraging FluxNinja Aperture, you'll be well-positioned to optimize resource utilization, ensuring your AI applications not only meet but exceed expectations in terms of performance and user experience.&lt;/p&gt;

&lt;p&gt;For a more in-depth understanding of FluxNinja Aperture, we invite you to explore our &lt;a href="https://docs.fluxninja.com/"&gt;Documentation&lt;/a&gt; or &lt;a href="https://app.fluxninja.com/sign-up"&gt;sign up&lt;/a&gt; for a 30-day trial. Additionally, join our vibrant &lt;a href="https://discord.gg/AaHdhAbNzH"&gt;Discord community&lt;/a&gt; to discuss best practices, ask questions, and engage in insightful discussions with like-minded individuals in the AI development field.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>machinelearning</category>
      <category>llm</category>
    </item>
    <item>
      <title>Protecting PostgreSQL with Adaptive Rate Limiting</title>
      <dc:creator>Sudhanshu Prajapati</dc:creator>
      <pubDate>Fri, 14 Jul 2023 10:12:19 +0000</pubDate>
      <link>https://dev.to/fluxninjahq/protecting-postgresql-with-adaptive-rate-limiting-5d78</link>
      <guid>https://dev.to/fluxninjahq/protecting-postgresql-with-adaptive-rate-limiting-5d78</guid>
      <description>&lt;p&gt;Even thirty years since its inception, PostgreSQL continues to gain traction, thriving in an environment of rapidly evolving open source projects. While some technologies appear and vanish swiftly, others, like the PostgreSQL database, prove their longevity, illustrating that they can withstand the test of time. It has become the preferred choice by many organizations for data storage, from general data storage to an asteroid tracking database. Companies are running PostgreSQL clusters with &lt;a href="https://www.postgresql.org/docs/current/history.html"&gt;petabytes of data&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Operating PostgreSQL on a large scale in a production environment can be challenging. Companies have experienced downtime and performance problems, resulting in financial losses and diminished trust, especially if the outages extend beyond a few hours. A case in point is the &lt;a href="https://about.gitlab.com/blog/2017/02/10/postmortem-of-database-outage-of-january-31/"&gt;GitLab database outage&lt;/a&gt; in Jan 2017. Though there were many attributes to how this outage happened, but to emphasize how overload can play a significant role, in their timeline, they explained how much time it took to control overload happening at that time, which cost them hours to control it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;± 19:00 UTC:&lt;/strong&gt; GitLab.com starts experiencing an increase in database load due to what we suspect was spam. In the week leading up to this event, GitLab.com had been experiencing similar problems, but not this severe. One of the problems this load caused was that many users were not able to post comments on issues and merge requests. Getting the load under control took several hours.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In their analysis of why GitLab was down for 18 hours, they cited one of the reasons as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Database load increase&lt;/strong&gt; - Which is caused by two events happening at the same time: an increase in spam and a process trying to remove a GitLab employee and their associated data. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These outages and performance issues could have been avoided with proper database protection in place. In this blog, we will explore the common issues faced by PostgreSQL, discuss what PostgreSQL protection entails, and delve into how FluxNinja has achieved it using Aperture.&lt;/p&gt;

&lt;h2&gt;
  
  
  PostgreSQL and Microservices
&lt;/h2&gt;

&lt;p&gt;PostgreSQL is a relational database that forms the backbone of many microservices-based applications. However, careful management of its performance and an understanding of its behaviors with respect to related services are vital to maintaining the system's stability and resilience.&lt;/p&gt;

&lt;p&gt;Most microservices-based applications today employ caching mechanisms to reduce the load on the database. The request hits the database only when the data is not found in the cache. Under normal operations, this enables efficiency and performance.&lt;/p&gt;

&lt;p&gt;However, this efficient harmony can be disrupted when the database experiences a slowdown or overload. Along similar lines, in the past, &lt;a href="https://www.honeycomb.io/blog/postmortem-rds-clogs-cache-refresh-crash-loops"&gt;HoneyComb.io experienced a partial API outage&lt;/a&gt; because of it.&lt;/p&gt;

&lt;p&gt;When database performance faltered, cache entries began to expire at an increased rate. This, in turn, triggered a significant surge in &lt;code&gt;goroutines&lt;/code&gt; attempting to refresh the cache simultaneously, which resulted in a resource-draining feedback loop that created system-wide strain. The outcome was a 'fire' that spread across the various microservices.&lt;/p&gt;

&lt;p&gt;From this incident, these were the essential findings,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Caching is A Double-Edged Sword&lt;/li&gt;
&lt;li&gt;It’s better to know the Cache Refresh Behavior, i.e., limiting the number of concurrent cache refresh operations can prevent feedback loops and keep the system stable even during periods of increased load on the database.&lt;/li&gt;
&lt;li&gt;Setup Proactive Alerting and Observability for the Database&lt;/li&gt;
&lt;li&gt;By identifying potential issues early, swift corrective measures can be implemented, preventing minor issues from snowballing into major system disruptions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's examine how PostgreSQL performs in a multi-tenant setup; A multi-tenant architecture encapsulates distinct tenants, analogous to separate users or applications, within a shared PostgreSQL database cluster. While operating in isolation at the application level, each tenant contends for the same system resources - CPU cycles, memory, disk I/O - at the database level. The challenge here is performance isolation: ensuring that the resource-intensive operations of one tenant don't impede the performance experienced by others.&lt;/p&gt;

&lt;p&gt;In high-load scenarios or during concurrent execution of expensive queries, tenants can monopolize shared resources, causing significant performance degradation for others. Managing concurrency becomes a complex task, requiring careful allocation of shared resources to maintain system performance. For more information on similar issues, you can look into the challenges &lt;a href="https://blog.cloudflare.com/performance-isolation-in-a-multi-tenant-database-environment/"&gt;Cloudflare&lt;/a&gt; has faced. However, such issues could be solved by PostgreSQL protection and quota per tenant.&lt;/p&gt;

&lt;p&gt;We will explore more about what PostgreSQL Protection is, in the next sections. Before that, let’s understand the common PostgreSQL issues that generally degrade performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common PostgreSQL Issues
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Maxed-out Connections&lt;/strong&gt;: Exceeding the maximum allowed connections can result in performance lags. Too many simultaneous client connections often cause this. Connection pooling can help, but connection exhaustion may still occur.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spikes in Memory &amp;amp; CPU Usage&lt;/strong&gt;: Several factors can contribute to high memory and CPU usage:

&lt;ul&gt;
&lt;li&gt;Large or complex queries.&lt;/li&gt;
&lt;li&gt;A high number of simultaneous connections.&lt;/li&gt;
&lt;li&gt;Resource-intensive background processes.&lt;/li&gt;
&lt;li&gt;Multiple services refreshing their cache at the same time.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High Response Latency&lt;/strong&gt;: High CPU usage can delay PostgreSQL's response time, affecting service reliability and user experience. This latency, when combined with CPU spikes, could result in system failures and dropped connections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Poorly Optimized Queries&lt;/strong&gt;: These can monopolize the connection, leading to connection starvation. One poorly optimized query is enough to cause a bottleneck, and multiple such queries can exacerbate the problem. The GitHub &lt;a href="https://github.blog/2023-05-16-addressing-githubs-recent-availability-issues/"&gt;outage in May 2023&lt;/a&gt; and the SemaphoreCI outage are examples of the impact of inefficient queries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Corrupted Index&lt;/strong&gt;: This can lead to inaccurate query results or slow down data retrieval. It can also trigger unnecessary full table scans, straining CPU and memory resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Noisy Neighbor Problem&lt;/strong&gt;: In multi-tenant PostgreSQL setups, this issue arises when one tenant's high resource usage affects others' performance. Techniques like manual concurrency limiting and load shedding can help manage this. The &lt;a href="https://blog.cloudflare.com/performance-isolation-in-a-multi-tenant-database-environment/"&gt;Cloudflare case&lt;/a&gt; is an example of successfully handling this issue.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The performance issues we've discussed are common with PostgreSQL, and various strategies and tools can help tackle them. One such effective tool is Aperture. Now, let's explore how FluxNinja employed Aperture to navigate these PostgreSQL challenges successfully.&lt;/p&gt;

&lt;h2&gt;
  
  
  FluxNinja ARC’s Battle with PostgreSQL
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.fluxninja.com/"&gt;FluxNinja&lt;/a&gt; ARC is a cloud-based solution designed by FluxNinja that enhances the functionality of the Aperture platform. It provides an intuitive interface that simplifies the management of Aperture systems operating across various clusters.&lt;/p&gt;

&lt;p&gt;FluxNinja ARC, the interface for Aperture, offers key features including a user-friendly interface, flow analytics for traffic insights, alerting system, visualization tools, and a streamlined policy builder UI. Aperture itself is an advanced load management platform emphasizing observability, with features like Adaptive Service Protection, Intelligent Quota Management, Workload Prioritization, and Load-Based Auto Scaling.&lt;/p&gt;

&lt;p&gt;Let's concentrate specifically on services that engage with PostgreSQL, rather than delving into the broad scope of the entire cloud architecture.&lt;/p&gt;

&lt;p&gt;FluxNinja Cloud has two services, API Service and Agent Service. API services that deal with UI, Organization, Sign in and Sign up, and similar functionality. The agent service collects heartbeats &amp;amp; last sync status from the agents &amp;amp; controllers running on different clusters. It also collects details of policies attached to controllers. However, both services don't directly interact with PostgreSQL, instead Hasura mediates the connection between both. The presence of Hasura between PostgreSQL and services brings significant benefits, as it offloads various tasks such as observability, authorization, and simplification of the SQL workflow. That being said, let’s jump into the problems we encountered with PostgreSQL &amp;amp; Hasura.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gYx-XEwh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r47l4fm883zguaor252v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gYx-XEwh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r47l4fm883zguaor252v.png" alt="FluxNinja Cloud Architecture " width="800" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While investigating the issue of performance, we observed that there were too many requests from both the API and Agent services. This resulted in&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Surge in requests and increased latency, leading to poor user experience&lt;/li&gt;
&lt;li&gt;While Hasura serves as a GraphQL engine, there are instances where it can become a performance bottleneck, struggling to handle the sudden influx of requests and failing to cope with the volume of incoming traffic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o6CCciwn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/osuvpxmgp8adde3v0s1f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o6CCciwn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/osuvpxmgp8adde3v0s1f.png" alt="Aperture Grafana Dashboard" width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Diagram, sourced from the Grafana Dashboard, illustrates the latency and workload acceptance rate for individual workloads. Latency spikes surpass 250ms, and the acceptance rate decreases under high load conditions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To address these issues, we considered two solutions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Scaling:&lt;/strong&gt; - Scaling could remove Hasura acting as a bottleneck.

&lt;ul&gt;
&lt;li&gt;If we scale too much to handle more requests, we could end up putting too much stress on PostgreSQL. Sending the same heavy load directly to PostgreSQL, potentially overloading it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate Limiting:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;To prevent PostgreSQL from being overwhelmed, we could limit the rate of incoming requests.&lt;/li&gt;
&lt;li&gt;This approach has a downside: It will penalize all the requests coming towards PostgreSQL without the context of workload priorities. It doesn't differentiate between high and low-priority workloads.&lt;/li&gt;
&lt;li&gt;This means:

&lt;ul&gt;
&lt;li&gt;Low-priority requests can interfere with high-priority ones.&lt;/li&gt;
&lt;li&gt;If low-priority requests are more numerous, high-priority workloads could get delayed. Therefore, high-priority tasks could be penalized more frequently.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This concept of workload prioritization can be envisaged within a multi-tenant environment, where each service behaves akin to a tenant, and one tenant may have higher priority than another, analogous to one service over another. For instance, we wouldn't want the API service to be starved of request resources of PostgreSQL due to the agent service.&lt;/p&gt;

&lt;p&gt;API service requests should’ve priority over Agent Service to deliver the best user experience to the Individuals who are using the cloud product.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Aperture PostgreSQL Protection
&lt;/h2&gt;

&lt;p&gt;The challenge at hand was to devise a strategy that would prioritize workloads, mitigate the risk of overloading PostgreSQL, prevent Hasura from becoming a bottleneck, and keep the user experience consistent.&lt;/p&gt;

&lt;p&gt;Aperture appears to be the ideal solution for addressing these challenges, and several compelling reasons reinforce our belief in its suitability.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Aperture can do concurrency throttling if required.&lt;/li&gt;
&lt;li&gt;It is built around a similar idea of Congestion Avoidance; Rather than reactively shedding the load, congestion avoidance “smoothly” throttles traffic before load-induced performance degradation becomes an issue.&lt;/li&gt;
&lt;li&gt;For a Multi-tenant environment, we can ensure resource consumption and detect tenants who are exceeding quotas and do query throttling with the help of quota management.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this is possible because of Adaptive Load Scheduling.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Aperture Adaptive Load Scheduling (ALS)?
&lt;/h3&gt;

&lt;p&gt;ALS is designed to safeguard services by dynamically adjusting the request rates. It does this by analyzing various health signals like latency and error rates, along with metrics like JMX and DB connections.&lt;/p&gt;

&lt;p&gt;It also enables Workload Prioritization. Requests are classified and labeled through declarative rules, enabling the scheduler to identify the criticality of different tasks. Algorithms such as the token bucket and weighted-fair queuing are employed to prioritize crucial requests over background workloads, ensuring system stability and efficient resource utilization.&lt;/p&gt;

&lt;p&gt;Inspired by &lt;a href="https://en.wikipedia.org/wiki/PID_controller"&gt;PID controllers&lt;/a&gt;, Aperture is a closed-loop system leveraging algorithms such as &lt;a href="https://wiki.geant.org/pages/releaseview.action?pageId=121340614"&gt;TCP BBR&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Additive_increase/multiplicative_decrease"&gt;AIMD&lt;/a&gt;, and &lt;a href="https://en.wikipedia.org/wiki/CoDel"&gt;CoDel&lt;/a&gt;. It seamlessly interacts with auto-scaling and load balancing systems to ensure optimal performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aperture Policies
&lt;/h3&gt;

&lt;p&gt;Aperture is powered by policies. It is what defines a control circuit graph.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Aperture Policy&lt;br&gt;
A policy in Aperture is a way to programmatically define conditions and actions that the system should follow to maintain its stability. These policies are evaluated regularly, and if any deviations from desired behavior are detected, appropriate actions are taken to correct them. Think of it as a system's &lt;code&gt;rulebook&lt;/code&gt; that helps it make decisions and keep things running smoothly. Learn more about policies on official &lt;a href="https://docs.fluxninja.com/development/concepts/advanced/policy"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Hasura Auto Scale Policy
&lt;/h4&gt;

&lt;p&gt;To remove Hasura from the picture of bottleneck, it’s important to scale it as the load increases. It becomes easy to do it via &lt;a href="https://docs.fluxninja.com/development/reference/blueprints/load-scheduling/average-latency"&gt;Service Protection with Average Latency Feedback&lt;/a&gt; Blueprint in addition to the Auto Scale component. The reason behind using this policy is it detects traffic overloads and cascading failure build-up by comparing the real-time latency with its exponential moving average. Which can act as an accurate signal to actuate, that is when to do Auto Scale. The policy is defined with a &lt;code&gt;latency baseliner&lt;/code&gt; configured with a label matcher on source and operation type requests. This means if the latency deviates from EMA, then it will try to send a signal for overload, which auto scale will act on.&lt;/p&gt;

&lt;p&gt;Below is the Circuit diagram of the policy; based on the circuit, it’s easier to pass on the signal values to the Auto Scale component from the Adaptive Load Scheduler component for auto-scaling decisions. While the Service Protection circuit is helping us get all the metrics and signals required. Another way to do it is via PromQL based Service Protection, but Average Latency Feedback seemed more relevant for the current case.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;During overload and sudden spikes, Aperture detects the latency deviation from the EMA, which will act as a signal (Desired Load Multiplier) to scale until it returns to the state where it should be.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;
  &lt;strong&gt;Hasura Auto Scale Policy&lt;/strong&gt;
  &lt;br&gt;

&lt;pre&gt;&lt;code&gt;&lt;span&gt;# yaml-language-server: $schema=../../../../../../aperture/blueprints/policies/service-protection/average-latency/gen/definitions.json&lt;/span&gt;
&lt;span&gt;policy&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
  &lt;span&gt;policy_name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;auto-scaling-hasura&lt;/span&gt;
  &lt;span&gt;components&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
    &lt;span&gt;-&lt;/span&gt; &lt;span&gt;auto_scale&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
        &lt;span&gt;auto_scaler&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
          &lt;span&gt;dry_run&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;false&lt;/span&gt;
          &lt;span&gt;dry_run_config_key&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;dry_run&lt;/span&gt;
          &lt;span&gt;scale_in_controllers&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
            &lt;span&gt;-&lt;/span&gt; &lt;span&gt;alerter&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;alert_name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;Periodic scale in intended&lt;/span&gt;
              &lt;span&gt;controller&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;periodic&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                  &lt;span&gt;period&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;60s&lt;/span&gt;
                  &lt;span&gt;scale_in_percentage&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;10&lt;/span&gt;
          &lt;span&gt;scale_out_controllers&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
            &lt;span&gt;-&lt;/span&gt; &lt;span&gt;alerter&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;alert_name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;Load based scale out intended&lt;/span&gt;
              &lt;span&gt;controller&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;gradient&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                  &lt;span&gt;in_ports&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                    &lt;span&gt;setpoint&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                      &lt;span&gt;constant_signal&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                        &lt;span&gt;value&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;1&lt;/span&gt;
                    &lt;span&gt;signal&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                      &lt;span&gt;signal_name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;DESIRED_LOAD_MULTIPLIER&lt;/span&gt;
                  &lt;span&gt;parameters&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                    &lt;span&gt;slope&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;-1&lt;/span&gt;
          &lt;span&gt;scaling_backend&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
            &lt;span&gt;kubernetes_replicas&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
              &lt;span&gt;kubernetes_object_selector&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;agent_group&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;default&lt;/span&gt;
                &lt;span&gt;api_version&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;apps/v1&lt;/span&gt;
                &lt;span&gt;kind&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;Deployment&lt;/span&gt;
                &lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;hasura&lt;/span&gt;
                &lt;span&gt;namespace&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;cloud&lt;/span&gt;
              &lt;span&gt;max_replicas&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;10"&lt;/span&gt;
              &lt;span&gt;min_replicas&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;1"&lt;/span&gt;
          &lt;span&gt;scaling_parameters&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
            &lt;span&gt;scale_in_alerter&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
              &lt;span&gt;alert_name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;Hasura auto scaler is scaling in&lt;/span&gt;
            &lt;span&gt;scale_in_cooldown&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;40s&lt;/span&gt;
            &lt;span&gt;scale_out_alerter&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
              &lt;span&gt;alert_name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;Hasura auto scaler is scaling out&lt;/span&gt;
            &lt;span&gt;scale_out_cooldown&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;30s&lt;/span&gt;
  &lt;span&gt;resources&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
    &lt;span&gt;flow_control&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
      &lt;span&gt;classifiers&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
        &lt;span&gt;-&lt;/span&gt; &lt;span&gt;selectors&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
            &lt;span&gt;-&lt;/span&gt; &lt;span&gt;service&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;hasura.cloud.svc.cluster.local&lt;/span&gt;
              &lt;span&gt;control_point&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;ingress&lt;/span&gt;
          &lt;span&gt;rego&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
            &lt;span&gt;labels&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
              &lt;span&gt;source&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;telemetry&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;true&lt;/span&gt;
              &lt;span&gt;operation&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;telemetry&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;true&lt;/span&gt;
            &lt;span&gt;module&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;|&lt;/span&gt;
              &lt;span&gt;package hasura_example&lt;/span&gt;
              &lt;span&gt;source = input.attributes.source.source_fqdns[0]&lt;/span&gt;
              &lt;span&gt;operation = graphql.parse_query(input.parsed_body.query).Operations[_].Operation&lt;/span&gt;
  &lt;span&gt;service_protection_core&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
    &lt;span&gt;dry_run&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;true&lt;/span&gt;
    &lt;span&gt;adaptive_load_scheduler&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
      &lt;span&gt;load_scheduler&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
        &lt;span&gt;selectors&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
          &lt;span&gt;-&lt;/span&gt; &lt;span&gt;control_point&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;ingress&lt;/span&gt;
            &lt;span&gt;service&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;hasura.cloud.svc.cluster.local&lt;/span&gt;
        &lt;span&gt;scheduler&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
          &lt;span&gt;workloads&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
            &lt;span&gt;-&lt;/span&gt; &lt;span&gt;label_matcher&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;match_labels&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                  &lt;span&gt;source&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;api-service.cloud.svc.cluster.local"&lt;/span&gt;
              &lt;span&gt;parameters&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;priority&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;250"&lt;/span&gt;
              &lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;api-service"&lt;/span&gt;
            &lt;span&gt;-&lt;/span&gt; &lt;span&gt;label_matcher&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;match_labels&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                  &lt;span&gt;source&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;agent-service.cloud.svc.cluster.local"&lt;/span&gt;
                  &lt;span&gt;operation&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;mutation"&lt;/span&gt;
              &lt;span&gt;parameters&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;priority&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;100"&lt;/span&gt;
              &lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;agent-service-mutation"&lt;/span&gt;
            &lt;span&gt;-&lt;/span&gt; &lt;span&gt;label_matcher&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;match_labels&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                  &lt;span&gt;source&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;agent-service.cloud.svc.cluster.local"&lt;/span&gt;
                  &lt;span&gt;operation&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;query"&lt;/span&gt;
              &lt;span&gt;parameters&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;priority&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;50"&lt;/span&gt;
              &lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;agent-service-query"&lt;/span&gt;
  &lt;span&gt;latency_baseliner&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
    &lt;span&gt;latency_tolerance_multiplier&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;1.1&lt;/span&gt;
    &lt;span&gt;flux_meter&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
      &lt;span&gt;selectors&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
        &lt;span&gt;-&lt;/span&gt; &lt;span&gt;control_point&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;ingress&lt;/span&gt;
          &lt;span&gt;service&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;hasura.cloud.svc.cluster.local&lt;/span&gt;
          &lt;span&gt;label_matcher&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
            &lt;span&gt;match_labels&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
              &lt;span&gt;operation&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;query"&lt;/span&gt;
              &lt;span&gt;source&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;api-service.cloud.svc.cluster.local"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sqD4sY3x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qw0syl3clnjnec0n8rxu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sqD4sY3x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qw0syl3clnjnec0n8rxu.png" alt="FluxNinja Auto Scaling Policy Circuit" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  PostgreSQL Service Protection Policy
&lt;/h4&gt;

&lt;p&gt;To shield the PostgreSQL from overload and sudden spikes, we created a &lt;a href="https://docs.fluxninja.com/development/reference/blueprints/load-scheduling/postgresql"&gt;PostgreSQL Protection Blueprint&lt;/a&gt;. Blueprints already do the heavy lifting with pre-configured InfraMeters for telemetry collection. InfraMeters are configured to add OpenTelemetry Collectors read more about &lt;a href="https://docs.fluxninja.com/development/integrations/metrics/"&gt;Fedding custom metrics in Aperture.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The blueprint revolves around two critical metrics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Max Connections on PostgreSQL:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PromQL Query:&lt;/strong&gt; &lt;code&gt;(sum(postgresql_backends) / sum(postgresql_connection_max)) * 100&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;This query computes the percentage of maximum connections currently in use by PostgreSQL, providing real-time insight into the connection load.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CPU overload confirmation:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PromQL Query:&lt;/strong&gt; &lt;code&gt;avg(k8s_pod_cpu_utilization_ratio{k8s_statefulset_name="hasura-postgresql"})&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;This query is employed to track the CPU utilization of the PostgreSQL service, housed within a Kubernetes &lt;code&gt;statefulSet&lt;/code&gt; named &lt;code&gt;hasura-postgresql&lt;/code&gt;. When a potential CPU overload is detected, this metric serves as a signal to activate the Adaptive Load Scheduler.&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;&lt;em&gt;These queries could be rewritten according to the problem requirement; for example, if you’re using a deployment instead of a &lt;code&gt;statefulset&lt;/code&gt;, then use the k8s_deployment_name metrics to configure it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Below is the circuit diagram of how this policy will work. It is fairly straightforward, it will use the &lt;code&gt;PromQL&lt;/code&gt; query to evaluate the confirmatory signal and set point which is % of max connection to enable Adaptive Load Scheduler. Under normal conditions, all the workloads are given a fair share.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6vOWMOEb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xvlpei40ovvtkavmtdje.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6vOWMOEb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xvlpei40ovvtkavmtdje.png" alt="FluxNinja PostgreSQL Protection Policy" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
  &lt;strong&gt;PostgreSQL Protection Policy&lt;/strong&gt;
  &lt;br&gt;

&lt;pre&gt;&lt;code&gt;&lt;span&gt;# yaml-language-server: $schema=../../../../../../aperture/blueprints/policies/service-protection/postgresql/gen/definitions.json&lt;/span&gt;
&lt;span&gt;policy&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
  &lt;span&gt;policy_name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;workload-prioritization-postgres&lt;/span&gt;
  &lt;span&gt;setpoint&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;70&lt;/span&gt;
  &lt;span&gt;postgresql&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
    &lt;span&gt;endpoint&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;hasura-postgresql.cloud.svc.cluster.local:5432&lt;/span&gt;
    &lt;span&gt;username&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;postgres&lt;/span&gt;
    &lt;span&gt;password&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;DevPassword&lt;/span&gt;
    &lt;span&gt;collection_interval&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;1s&lt;/span&gt;
    &lt;span&gt;tls&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
      &lt;span&gt;insecure&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;true&lt;/span&gt;
  &lt;span&gt;resources&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
    &lt;span&gt;flow_control&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
      &lt;span&gt;classifiers&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
        &lt;span&gt;-&lt;/span&gt; &lt;span&gt;selectors&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
            &lt;span&gt;-&lt;/span&gt; &lt;span&gt;service&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;hasura.cloud.svc.cluster.local&lt;/span&gt;
              &lt;span&gt;control_point&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;ingress&lt;/span&gt;
          &lt;span&gt;rego&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
            &lt;span&gt;labels&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
              &lt;span&gt;source&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;telemetry&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;true&lt;/span&gt;
              &lt;span&gt;operation&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;telemetry&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;true&lt;/span&gt;
            &lt;span&gt;module&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;|&lt;/span&gt;
              &lt;span&gt;package hasura_example&lt;/span&gt;
              &lt;span&gt;source = input.attributes.source.source_fqdns[0]&lt;/span&gt;
              &lt;span&gt;operation = graphql.parse_query(input.parsed_body.query).Operations[_].Operation&lt;/span&gt;
  &lt;span&gt;service_protection_core&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
    &lt;span&gt;dry_run&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;false&lt;/span&gt;
    &lt;span&gt;cpu_overload_confirmation&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
      &lt;span&gt;query_string&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;avg(k8s_pod_cpu_utilization_ratio{k8s_statefulset_name="hasura-postgresql"})&lt;/span&gt;
      &lt;span&gt;threshold&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;2.1&lt;/span&gt;
      &lt;span&gt;operator&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;gte&lt;/span&gt;
    &lt;span&gt;adaptive_load_scheduler&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
      &lt;span&gt;load_scheduler&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
        &lt;span&gt;selectors&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
          &lt;span&gt;-&lt;/span&gt; &lt;span&gt;control_point&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;ingress&lt;/span&gt;
            &lt;span&gt;service&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;hasura.cloud.svc.cluster.local&lt;/span&gt;
        &lt;span&gt;scheduler&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
          &lt;span&gt;workloads&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
            &lt;span&gt;-&lt;/span&gt; &lt;span&gt;label_matcher&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;match_labels&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                  &lt;span&gt;source&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;api-service.cloud.svc.cluster.local"&lt;/span&gt;
              &lt;span&gt;parameters&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;priority&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;255"&lt;/span&gt;
              &lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;api-service"&lt;/span&gt;
            &lt;span&gt;-&lt;/span&gt; &lt;span&gt;label_matcher&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;match_labels&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                  &lt;span&gt;source&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;agent-service.cloud.svc.cluster.local"&lt;/span&gt;
                  &lt;span&gt;operation&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;mutation"&lt;/span&gt;
              &lt;span&gt;parameters&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;priority&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;100"&lt;/span&gt;
              &lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;agent-service-mutation"&lt;/span&gt;
            &lt;span&gt;-&lt;/span&gt; &lt;span&gt;label_matcher&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;match_labels&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                  &lt;span&gt;source&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;agent-service.cloud.svc.cluster.local"&lt;/span&gt;
                  &lt;span&gt;operation&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;query"&lt;/span&gt;
              &lt;span&gt;parameters&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
                &lt;span&gt;priority&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;50"&lt;/span&gt;
              &lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;"&lt;/span&gt;&lt;span&gt;agent-service-query"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/p&gt;

&lt;p&gt;When overload occurs at PostgreSQL, the policy performs adaptive load scheduling with workload prioritization while ensuring the % of max connections shouldn’t exceed 70 (the defined set point in the policy).&lt;/p&gt;

&lt;p&gt;Workload Prioritization will ensure that the API service request is given more priority over Agent service requests to PostgreSQL, this is achieved using label matcher, which is defined in the policy &lt;code&gt;Source&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Operation type based Workload Prioritization is also configured similarly, which uses the label matcher &lt;code&gt;Operation&lt;/code&gt; to decide the request priority in case of Agent Service requests. When multiple requests come from the Agent service, mutation requests are prioritized over query requests. Above all, the API service gets the highest priority&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One thing to note: The operation type label is extracted using a classifier defined in the policy; A Classifier is defined in the policy to extract the operation type using the Rego module.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So, the order of Prioritization follows. API Service Requests &amp;gt; Agent Service &lt;code&gt;Mutation Operation&lt;/code&gt; &amp;gt; Agent Service &lt;code&gt;Query&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Why Mutation over Query Requests?
&lt;/h4&gt;

&lt;p&gt;A query operation is a read operation, while a mutation operation can write, update, or delete data. In the case of FluxNinja, mutation operations from agent services occur after a series of query operations. To prevent discarding the work already completed during the preceding queries, mutation operations are given higher priority over queries. This prioritization ensures that the effort invested in the preceding queries is not wasted.&lt;/p&gt;

&lt;p&gt;This way, Aperture ensures how the system should react when there is a high load. All the components defined in the policy work together to provide PostgreSQL protection in overload situations while ensuring high-priority workloads are respected.&lt;/p&gt;

&lt;p&gt;All of this is done even before the request reaches PostgreSQL or Hasura, this saves resources during overload situations.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: Where a policy is acting, it can be easily identified based on the control point.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Policies in Action
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TmH66xTe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c6tf4zibp4j35kr7vwa2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TmH66xTe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c6tf4zibp4j35kr7vwa2.png" alt="FluxNinja Arch with Aperture Policy" width="800" height="319"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hasura's policy maintains latency within the EMA boundaries. If latency deviates, it will scale Hasura to manage the influx of requests. A second policy monitors that the maximum connection usage does not surpass 70%. If it does, and CPU usage is high, Aperture initiates Adaptive Load Scheduling with Workload Prioritization, ensuring the user experience is sustained.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;The Diagram is sourced from the Grafana Dashboard, displaying the latency and workload acceptance rate for individual workloads with Aperture policies implemented. Latency remains within the desired range, and the acceptance rate remains high, even during periods of high load.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;By leveraging Aperture's capabilities and using these policies, FluxNinja achieved effective PostgreSQL protection, addressed performance issues, and ensured user experience even during sudden traffic spikes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenges
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Identifying the appropriate confirmatory signal for PostgreSQL protection can vary across different scenarios. In our case, the common one which proved helpful was obtaining the percentage of maximum connections used and tracking CPU usage.

&lt;ul&gt;
&lt;li&gt;High CPU usage could indicate the execution of an expensive query or many open connections.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Gathering both metrics, i.e., number of connections and CPU usage, clarified the situation to a considerable extent. Although these are not the most exhaustive metrics, they served our purpose adequately.&lt;/li&gt;
&lt;li&gt;Determining the right metrics is a task, especially when these metrics are being used as signals in the policy. For us, the situation was streamlined with the use of telemetry collectors, which gathered all the required metrics. However, for more advanced scenarios, for instance, determining which query is expensive or time-consuming, additional thought and planning might be required on how to acquire these details.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Aperture operates within the FluxNinja infrastructure, safeguarding it against sudden spikes and overload scenarios. Although it currently focuses on protecting PostgreSQL, there are certainly more opportunities for us in the future to address other issues with it. The effectiveness of Aperture has been evident in our work on the product designed for the next generation of Reliability Teams.&lt;/p&gt;

&lt;p&gt;During the process of writing this blog and researching various postmortems and reports, it has become apparent that companies need a solution like Aperture. These insights underline the importance of Aperture in confronting the issues highlighted in many organizational blogs.&lt;/p&gt;

&lt;p&gt;To learn more about Aperture, please visit our &lt;a href="https://github.com/fluxninja/aperture"&gt;GitHub repository&lt;/a&gt; and &lt;a href="https://docs.fluxninja.com"&gt;documentation site&lt;/a&gt;. You can also join our &lt;a href="https://fluxninja-aperture.slack.com/signup#/domain-signup"&gt;Slack community&lt;/a&gt; to discuss best practices, ask questions, and engage in discussions on reliability management.&lt;/p&gt;

&lt;p&gt;For further reading on PostgreSQL protection and related topics, we recommend exploring the following resources:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://docs.fluxninja.com/"&gt;Aperture Documentation&lt;/a&gt;: Dive deeper into the features and capabilities of Aperture for effective load management and protection.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.honeycomb.io/blog/postmortem-rds-clogs-cache-refresh-crash-loops"&gt;Postmortem: RDS Clogs &amp;amp; Cache-Refresh Crash Loops | Honeycomb&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.blog/2023-05-16-addressing-githubs-recent-availability-issues/"&gt;Addressing GitHub’s recent availability issues&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://blog.cloudflare.com/performance-isolation-in-a-multi-tenant-database-environment/"&gt;Performance isolation in a multi-tenant database environment&lt;/a&gt; &lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>opensource</category>
      <category>database</category>
      <category>performance</category>
      <category>microservices</category>
    </item>
  </channel>
</rss>
