<?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: Alok Deep</title>
    <description>The latest articles on DEV Community by Alok Deep (@alok1663).</description>
    <link>https://dev.to/alok1663</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4097920%2F13eb2106-9686-4250-8c70-e76401460e96.png</url>
      <title>DEV Community: Alok Deep</title>
      <link>https://dev.to/alok1663</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alok1663"/>
    <language>en</language>
    <item>
      <title>How DEV.to Can Cut TTFB by 98% and Scale Throughput with Edge Caching</title>
      <dc:creator>Alok Deep</dc:creator>
      <pubDate>Thu, 03 Sep 2026 09:55:20 +0000</pubDate>
      <link>https://dev.to/alok1663/how-devto-can-cut-ttfb-by-98-and-scale-throughput-with-edge-caching-335n</link>
      <guid>https://dev.to/alok1663/how-devto-can-cut-ttfb-by-98-and-scale-throughput-with-edge-caching-335n</guid>
      <description>&lt;p&gt;dev.to is one of the largest developer communities on the web, serving millions of technical articles, discussions, and feeds each month. The platform runs on &lt;a href="https://github.com/forem/forem" rel="noopener noreferrer"&gt;Forem&lt;/a&gt;, an open-source software stack built on Ruby on Rails, PostgreSQL, Redis, and Sidekiq.&lt;/p&gt;

&lt;p&gt;Forem's Rails architecture provides productive developer tooling and clean domain abstractions. However, serving dynamic server-rendered HTML through Puma workers introduces measurable latency, especially under high concurrency or traffic surges.&lt;/p&gt;

&lt;p&gt;We ran a performance and infrastructure audit against production dev.to endpoints using an automated benchmarking suite. Here is a breakdown of origin bottlenecks, empirical benchmark results, and how an in-memory edge proxy like ApexCache drops time to first byte (TTFB) from 640ms down to 11ms while offloading 100% of read traffic from origin Rails workers.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Rails request lifecycle on Forem
&lt;/h2&gt;

&lt;p&gt;When a user requests a public page like &lt;code&gt;https://dev.to/videos&lt;/code&gt; or &lt;code&gt;https://dev.to/challenges&lt;/code&gt;, the request travels through several layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP Request ──► Cloudflare / CDN ──► Origin Load Balancer ──► Puma Worker (Ruby)
                                                                 ├── Controller action &amp;amp; params validation
                                                                 ├── PostgreSQL query (articles, user relations)
                                                                 ├── Fragment cache checks (Redis)
                                                                 └── ERB / ViewComponent HTML rendering
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even with Redis fragment caching and database indexing, every request hitting the Rails origin must:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Allocate an execution thread inside a Puma worker process.&lt;/li&gt;
&lt;li&gt;Initialize request context, parse cookies, and evaluate session state.&lt;/li&gt;
&lt;li&gt;Fetch missing cache fragments or execute database queries via ActiveRecord.&lt;/li&gt;
&lt;li&gt;Render string templates and construct the complete HTTP payload.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Under light traffic, a modern Rails server responds in 300ms to 600ms. But because Ruby is interpreted and execution threads hold memory, Puma worker pools quickly saturate when hundreds of concurrent requests arrive simultaneously.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Benchmark methodology: direct origin vs edge acceleration
&lt;/h2&gt;

&lt;p&gt;We audited dev.to across 8 representative public routes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/&lt;/code&gt; (Homepage feed)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/challenges&lt;/code&gt; (Community coding challenges)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/++&lt;/code&gt; (Community member index)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/videos&lt;/code&gt; (Video content feed)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/deved&lt;/code&gt; (Developer Education listings)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/help&lt;/code&gt; (Help &amp;amp; FAQ center)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/advertise&lt;/code&gt; (Advertising information)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/organizations&lt;/code&gt; (Developer organization index)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The test ran in two phases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Phase 1 (Direct Origin Baseline):&lt;/strong&gt; Paced baseline queries (concurrency 2, 100ms pacing delay) directly to &lt;code&gt;https://www.dev.to&lt;/code&gt; to measure native origin response times without triggering rate limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 2 &amp;amp; 3 (In-Memory Edge Acceleration):&lt;/strong&gt; The same endpoints routed through an ApexCache edge proxy instance, warmed with a single pass, then stress-tested across 25,000 requests at a concurrency of 40 worker threads.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Empirical benchmark results
&lt;/h2&gt;

&lt;p&gt;The benchmark measured TTFB, end-to-end latency, throughput capacity, and tail latencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Telemetry comparison matrix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evaluation parameter&lt;/th&gt;
&lt;th&gt;Before (Direct origin)&lt;/th&gt;
&lt;th&gt;After (ApexCache edge)&lt;/th&gt;
&lt;th&gt;Delta / Improvement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sampled request load&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;40 requests&lt;/td&gt;
&lt;td&gt;25,000 requests&lt;/td&gt;
&lt;td&gt;625x load scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Worker concurrency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2 workers&lt;/td&gt;
&lt;td&gt;40 workers&lt;/td&gt;
&lt;td&gt;High-concurrency burst&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TTFB (Mean)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;639.9 ms&lt;/td&gt;
&lt;td&gt;11.0 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;58.3x faster (-98.3%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TTFB (Median P50)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;420.0 ms&lt;/td&gt;
&lt;td&gt;10.5 ms&lt;/td&gt;
&lt;td&gt;-409.5 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TTFB (P95 Tail)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.85 s&lt;/td&gt;
&lt;td&gt;18.1 ms&lt;/td&gt;
&lt;td&gt;-1.83 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TTFB (P99 Tail)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2.29 s&lt;/td&gt;
&lt;td&gt;23.1 ms&lt;/td&gt;
&lt;td&gt;-2.27 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;E2E Latency (Mean)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;660.0 ms&lt;/td&gt;
&lt;td&gt;15.6 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;42.3x faster (-97.6%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;E2E Latency (Median P50)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;434.6 ms&lt;/td&gt;
&lt;td&gt;14.7 ms&lt;/td&gt;
&lt;td&gt;-419.9 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;E2E Latency (P95 Tail)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.85 s&lt;/td&gt;
&lt;td&gt;27.1 ms&lt;/td&gt;
&lt;td&gt;-1.82 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;E2E Latency (P99 Tail)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2.51 s&lt;/td&gt;
&lt;td&gt;34.2 ms&lt;/td&gt;
&lt;td&gt;-2.48 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Origin offload ratio&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.0% (All hit origin)&lt;/td&gt;
&lt;td&gt;100.0%&lt;/td&gt;
&lt;td&gt;100% backend offload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Throughput capacity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2.4 req/s&lt;/td&gt;
&lt;td&gt;2,556.1 req/s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,086x capacity&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Per-endpoint latency breakdown
&lt;/h3&gt;

&lt;p&gt;Origin response times varied substantially by page complexity:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Endpoint path&lt;/th&gt;
&lt;th&gt;Origin TTFB&lt;/th&gt;
&lt;th&gt;Edge TTFB&lt;/th&gt;
&lt;th&gt;Origin E2E&lt;/th&gt;
&lt;th&gt;Edge E2E&lt;/th&gt;
&lt;th&gt;Mean payload&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/&lt;/code&gt; (Homepage)&lt;/td&gt;
&lt;td&gt;1,090.0 ms&lt;/td&gt;
&lt;td&gt;14.3 ms&lt;/td&gt;
&lt;td&gt;1,200.0 ms&lt;/td&gt;
&lt;td&gt;23.7 ms&lt;/td&gt;
&lt;td&gt;253.7 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/challenges&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;689.1 ms&lt;/td&gt;
&lt;td&gt;11.7 ms&lt;/td&gt;
&lt;td&gt;703.8 ms&lt;/td&gt;
&lt;td&gt;18.7 ms&lt;/td&gt;
&lt;td&gt;171.5 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/++&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;767.2 ms&lt;/td&gt;
&lt;td&gt;12.9 ms&lt;/td&gt;
&lt;td&gt;780.7 ms&lt;/td&gt;
&lt;td&gt;18.6 ms&lt;/td&gt;
&lt;td&gt;141.1 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/videos&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;921.2 ms&lt;/td&gt;
&lt;td&gt;10.3 ms&lt;/td&gt;
&lt;td&gt;924.7 ms&lt;/td&gt;
&lt;td&gt;14.1 ms&lt;/td&gt;
&lt;td&gt;64.8 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/organizations&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;538.9 ms&lt;/td&gt;
&lt;td&gt;9.9 ms&lt;/td&gt;
&lt;td&gt;547.4 ms&lt;/td&gt;
&lt;td&gt;13.0 ms&lt;/td&gt;
&lt;td&gt;56.9 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/help&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;463.1 ms&lt;/td&gt;
&lt;td&gt;9.4 ms&lt;/td&gt;
&lt;td&gt;467.4 ms&lt;/td&gt;
&lt;td&gt;12.5 ms&lt;/td&gt;
&lt;td&gt;48.8 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/advertise&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;337.8 ms&lt;/td&gt;
&lt;td&gt;9.5 ms&lt;/td&gt;
&lt;td&gt;339.3 ms&lt;/td&gt;
&lt;td&gt;12.0 ms&lt;/td&gt;
&lt;td&gt;42.3 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/deved&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;310.7 ms&lt;/td&gt;
&lt;td&gt;9.6 ms&lt;/td&gt;
&lt;td&gt;321.5 ms&lt;/td&gt;
&lt;td&gt;12.3 ms&lt;/td&gt;
&lt;td&gt;43.2 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On the homepage (&lt;code&gt;/&lt;/code&gt;), origin TTFB was 1.09 seconds. Edge acceleration delivered the same fully-rendered HTML document in 14.3ms.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Why tail latencies matter for dev.to
&lt;/h2&gt;

&lt;p&gt;While average latency paints a broad picture, tail latency (P95 and P99) dictates real user experience.&lt;/p&gt;

&lt;p&gt;During the direct origin test, P99 TTFB reached &lt;strong&gt;2.29 seconds&lt;/strong&gt;, and P99 end-to-end latency reached &lt;strong&gt;2.51 seconds&lt;/strong&gt;. When a user on a mobile device or high-latency connection visits a page with a 2.5-second initial document delay, browser parsing is stalled. CSS, JavaScript, and fonts cannot start downloading until the initial HTML document arrives.&lt;/p&gt;

&lt;p&gt;With edge acceleration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;P95 TTFB dropped from 1,850ms to 18.1ms.&lt;/li&gt;
&lt;li&gt;P99 E2E latency dropped from 2,510ms to 34.2ms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The edge proxy truncates the latency tail because requests are served directly from RAM without database lookups, lock contention, or garbage collection pauses.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Business and user experience impact
&lt;/h2&gt;

&lt;p&gt;Using established web performance research from Google, Akamai, and Deloitte Digital, we evaluated what saving 644ms per request means for dev.to:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Cumulative user wait time eliminated
&lt;/h3&gt;

&lt;p&gt;At 10,000,000 monthly page views, reducing mean response time by 644.4ms eliminates &lt;strong&gt;1,790 hours of human waiting time every month&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;$$\text{Hours Saved} = \frac{10,000,000 \times 0.6444\text{s}}{3,600} \approx 1,790\text{ hours/month}$$&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Projected conversion lift (+6.4%)
&lt;/h3&gt;

&lt;p&gt;Deloitte's study on retail and publishing sites established that every 100ms improvement in page speed increases conversion events (signups, newsletter subscriptions, and click-throughs) by ~1.0%. A 644ms speedup yields a projected &lt;strong&gt;+6.4% lift&lt;/strong&gt; in user engagement and registration completion.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Bounce rate reduction (-12.9%)
&lt;/h3&gt;

&lt;p&gt;Google's Core Web Vitals research shows that reducing initial page load times below 1 second cuts immediate abandonment. Faster page responses across community feeds prevent users from bouncing before articles render.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Core Web Vitals compliance
&lt;/h3&gt;

&lt;p&gt;Google's search algorithm classifies TTFB into three tiers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Good: Under 200ms&lt;/li&gt;
&lt;li&gt;Needs Improvement: 200ms to 800ms&lt;/li&gt;
&lt;li&gt;Poor: Over 800ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;dev.to's origin average of 639.9ms falls into the "Needs Improvement" bracket, with pages like &lt;code&gt;/&lt;/code&gt; (1,090ms) and &lt;code&gt;/videos&lt;/code&gt; (921ms) falling into "Poor". Edge acceleration moves all audited routes into the &lt;strong&gt;Good (&amp;lt;20ms)&lt;/strong&gt; bracket, improving search engine crawl efficiency and mobile search placement.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Financial and infrastructure savings (TCO)
&lt;/h2&gt;

&lt;p&gt;Running dynamic Rails applications at scale requires substantial server resources. Modeling dev.to's infrastructure requirements at 10,000,000 monthly requests demonstrates the cost difference between origin compute and edge caching:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cost category&lt;/th&gt;
&lt;th&gt;Self-managed origin&lt;/th&gt;
&lt;th&gt;ApexCache managed edge&lt;/th&gt;
&lt;th&gt;Monthly savings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Application compute (vCPUs)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$131.40 (4 vCPUs)&lt;/td&gt;
&lt;td&gt;$32.85 (1 vCPU)&lt;/td&gt;
&lt;td&gt;$98.55&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Outbound egress bandwidth&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$78.43 (980 GB)&lt;/td&gt;
&lt;td&gt;$0.00 (Served at edge)&lt;/td&gt;
&lt;td&gt;$78.43&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database read replicas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$350.00 (1-2 RDS instances)&lt;/td&gt;
&lt;td&gt;$0.00 (DB reads offloaded)&lt;/td&gt;
&lt;td&gt;$350.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DevOps engineering upkeep&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$2,000.00 (0.25 FTE labor)&lt;/td&gt;
&lt;td&gt;$0.00 (Automated rules)&lt;/td&gt;
&lt;td&gt;$2,000.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ApexCache platform fee&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0.00&lt;/td&gt;
&lt;td&gt;$449.00 (Growth Tier)&lt;/td&gt;
&lt;td&gt;-$449.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total monthly operating TCO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$2,559.83&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$481.85&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$2,077.98 / month&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Annual net savings: &lt;strong&gt;$24,935.75 per year&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Total cost reduction: &lt;strong&gt;-81.2%&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Return on investment multiple: &lt;strong&gt;5.6x net value multiple&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The largest financial saving is not raw server compute; it is developer labor. Managing custom Redis caching layers, writing custom cache sweepers, and debugging cache synchronization issues consumes approximately 10 hours per week of senior platform engineering time (~0.25 FTE). Shifting caching to an automated edge layer frees engineers to focus on product development.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Deployment options: zero-code CNAME vs tagged Rails purging
&lt;/h2&gt;

&lt;p&gt;Teams can introduce edge acceleration through two paths: a zero-code DNS change, or precision application-level cache tagging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option A: Zero-code CNAME DNS routing (Fast path)
&lt;/h3&gt;

&lt;p&gt;The fastest deployment requires no Rails code changes. You route incoming traffic through ApexCache at the DNS level:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add your domain in the ApexCache dashboard:&lt;/strong&gt; Enter &lt;code&gt;www.dev.to&lt;/code&gt; and specify your origin server (for example, &lt;code&gt;origin.dev.to&lt;/code&gt; or your AWS Application Load Balancer endpoint).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add DNS records at your DNS provider (Route 53, Cloudflare, or NS1):&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Record type&lt;/th&gt;
&lt;th&gt;Name / Host&lt;/th&gt;
&lt;th&gt;Target / Value&lt;/th&gt;
&lt;th&gt;TTL&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TXT&lt;/td&gt;
&lt;td&gt;&lt;code&gt;_apexcache-challenge.www&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;apex-verify=YOUR_TOKEN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;300s&lt;/td&gt;
&lt;td&gt;Domain ownership verification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CNAME&lt;/td&gt;
&lt;td&gt;&lt;code&gt;www&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cname.getapexcache.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;300s&lt;/td&gt;
&lt;td&gt;Set to DNS Only (grey cloud on Cloudflare)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Automated TLS termination:&lt;/strong&gt; ApexCache provisions and renews Let's Encrypt TLS 1.3 certificates automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure default path rules:&lt;/strong&gt; In the dashboard, configure micro-caching rules for public routes (&lt;code&gt;/&lt;/code&gt;, &lt;code&gt;/challenges&lt;/code&gt;, &lt;code&gt;/videos&lt;/code&gt;, &lt;code&gt;/organizations&lt;/code&gt;, &lt;code&gt;/help&lt;/code&gt;) with 60s to 300s TTLs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session bypass:&lt;/strong&gt; ApexCache automatically bypasses the cache when requests carry authentication cookies (such as &lt;code&gt;_forem_session&lt;/code&gt; or &lt;code&gt;remember_user_token&lt;/code&gt;), passing logged-in requests directly to origin Puma workers.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Option B: Application-level surrogate tagging in Rails (Precision path)
&lt;/h3&gt;

&lt;p&gt;For instant cache invalidation upon post publication, configure your Rails controllers to return surrogate keys (&lt;code&gt;X-Cache-Tags&lt;/code&gt;) and fire asynchronous webhooks on model updates.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Return surrogate keys in Rails controllers
&lt;/h4&gt;

&lt;p&gt;In your Rails controllers, return standard &lt;code&gt;Cache-Control&lt;/code&gt; headers along with an &lt;code&gt;X-Cache-Tags&lt;/code&gt; header identifying the entities rendered on the page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/articles_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArticlesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="vi"&gt;@articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;published&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;created_at: :desc&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Set public cache header for anonymous visitors&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
      &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Cache-Control'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'public, max-age=300, stale-while-revalidate=60'&lt;/span&gt;

      &lt;span class="c1"&gt;# Tag the response with collection and individual article identifiers&lt;/span&gt;
      &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'articles'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'feed:home'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="vi"&gt;@articles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;"article:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'X-Cache-Tags'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="c1"&gt;# Bypass caching for authenticated sessions&lt;/span&gt;
      &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Cache-Control'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'private, no-cache, no-store'&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="vi"&gt;@article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;friendly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
      &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Cache-Control'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'public, max-age=3600, stale-while-revalidate=120'&lt;/span&gt;
      &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'X-Cache-Tags'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"article:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,user:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Cache-Control'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'private, no-cache, no-store'&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 2: Invalidate tags when articles update
&lt;/h4&gt;

&lt;p&gt;Whenever an author edits an article or publishes a new post, trigger a single webhook to purge the affected tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/article.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Article&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;after_commit&lt;/span&gt; &lt;span class="ss"&gt;:purge_edge_cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;on: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;purge_edge_cache&lt;/span&gt;
    &lt;span class="c1"&gt;# Async background job using Sidekiq&lt;/span&gt;
    &lt;span class="no"&gt;PurgeEdgeCacheJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_later&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"article:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;PurgeEdgeCacheJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_later&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"feed:home"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;saved_change_to_published?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The background worker sends a &lt;code&gt;POST&lt;/code&gt; request to the ApexCache purge endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/jobs/purge_edge_cache_job.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PurgeEdgeCacheJob&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationJob&lt;/span&gt;
  &lt;span class="n"&gt;queue_as&lt;/span&gt; &lt;span class="ss"&gt;:default&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.getapexcache.com/api/v1/cache/invalidate'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Authorization'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Bearer &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'APEXCACHE_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'application/json'&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;tags: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;

    &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;use_ssl: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When an article is modified, the edge cache clears only the pages associated with &lt;code&gt;article:123&lt;/code&gt; across all global edge points of presence in under 150 milliseconds. The rest of the site cache remains completely intact.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Summary
&lt;/h2&gt;

&lt;p&gt;Rails is a productive framework for building rich web platforms, but server-rendered architectures naturally encounter latency and concurrency constraints when scaling public feeds.&lt;/p&gt;

&lt;p&gt;By placing an in-memory edge caching gateway in front of Forem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mean TTFB drops from 639.9ms to 11.0ms (58x faster).&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;P99 tail latency drops from 2.51s to 34.2ms.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Throughput capacity expands from 2.4 req/s to 2,556 req/s.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Origin server resources are offloaded 100% on cached routes.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platform operating costs decline by 81.2%, saving over $24,900 annually.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Offloading read traffic to edge memory lets Rails applications scale smoothly while preserving backend database and compute capacity for authenticated user interactions.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>performance</category>
      <category>ruby</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How to Scale PHP Applications Without Maxing Out PHP-FPM Pools</title>
      <dc:creator>Alok Deep</dc:creator>
      <pubDate>Tue, 01 Sep 2026 06:14:01 +0000</pubDate>
      <link>https://dev.to/alok1663/how-to-scale-php-applications-without-maxing-out-php-fpm-pools-14f2</link>
      <guid>https://dev.to/alok1663/how-to-scale-php-applications-without-maxing-out-php-fpm-pools-14f2</guid>
      <description>&lt;p&gt;PHP runs a massive percentage of web applications and APIs, from custom frameworks to large Laravel and Symfony monoliths. Modern PHP 8.x with JIT and OPcache executes code quickly, but production servers still struggle during sudden traffic surges.&lt;/p&gt;

&lt;p&gt;The root problem is rarely the CPU speed of PHP itself. The real bottleneck is the concurrency architecture of PHP-FPM (FastCGI Process Manager).&lt;/p&gt;

&lt;p&gt;Here is a breakdown of why PHP-FPM process pools saturate under load, why application-level Redis caches cannot fully protect the server, and how placing an edge reverse proxy in front of PHP drops TTFB to 12ms while offloading 95% of server traffic.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The PHP-FPM Process Model Bottleneck
&lt;/h2&gt;

&lt;p&gt;Unlike long-running Node.js event loops or Go goroutines, traditional PHP uses a shared-nothing execution model. Every HTTP request requires an isolated worker process.&lt;/p&gt;

&lt;p&gt;When a request arrives at Nginx:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Nginx passes the request over FastCGI to a free PHP-FPM worker.&lt;/li&gt;
&lt;li&gt;The worker boots the framework bootstrap files, autoloaders, environment configs, and database connection pools.&lt;/li&gt;
&lt;li&gt;The worker runs application logic, queries MySQL or PostgreSQL, and serializes the response.&lt;/li&gt;
&lt;li&gt;The worker tears down request state, frees memory, and waits for the next request.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP Request ──► Nginx (Reverse Proxy) ──► FastCGI Socket ──► PHP-FPM Worker Pool
                                                                  ├── Worker #1 (Busy: 180ms)
                                                                  ├── Worker #2 (Busy: 240ms)
                                                                  └── Worker #3 (Busy: 160ms)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;php-fpm.conf&lt;/code&gt;, the &lt;code&gt;pm.max_children&lt;/code&gt; directive defines the maximum number of concurrent workers. On an 8GB RAM server running a modern framework, each worker consumes 40MB to 70MB of memory, limiting &lt;code&gt;pm.max_children&lt;/code&gt; to roughly 60 to 100 workers.&lt;/p&gt;

&lt;p&gt;If an endpoint takes an average of 200ms to respond, 100 workers can handle a maximum of 500 requests per second:&lt;/p&gt;

&lt;p&gt;$$\text{Max Throughput} = \frac{\text{Worker Pool Size}}{\text{Average Response Time}} = \frac{100}{0.2\text{s}} = 500\text{ req/sec}$$&lt;/p&gt;

&lt;p&gt;When request volume spikes to 1,500 req/sec, the queue buffer (&lt;code&gt;listen.backlog&lt;/code&gt;) fills up immediately. Nginx throws &lt;code&gt;502 Bad Gateway&lt;/code&gt; and &lt;code&gt;504 Gateway Timeout&lt;/code&gt; errors, and the server runs out of memory.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Why Internal Redis Caching Falls Short
&lt;/h2&gt;

&lt;p&gt;Many teams try to solve this by adding Redis caching directly inside PHP controllers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="c1"&gt;// app/Http/Controllers/ProductController.php&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$cacheKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"product:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Check local Redis&lt;/span&gt;
    &lt;span class="nv"&gt;$cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cacheKey&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="nv"&gt;$cached&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="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cached&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'variants'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'categories'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;findOrFail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;setex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&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;While this reduces database query load, it does not fix PHP-FPM worker exhaustion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A PHP-FPM worker process is still occupied for the entire duration of the request.&lt;/li&gt;
&lt;li&gt;Framework routing, dependency injection containers, middleware pipelines, and JSON serialization still run on every hit.&lt;/li&gt;
&lt;li&gt;Even a 20ms Redis-cached response limits a 100-worker pool to 5,000 req/sec under perfect conditions, while consuming significant CPU cycles.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. The Solution: Edge Proxy Caching with Tagged Purging
&lt;/h2&gt;

&lt;p&gt;Instead of letting read requests reach PHP-FPM, place an edge proxy (such as ApexCache) directly in front of your application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client (Global) ──► ApexCache Edge Gateway
                          ├── GET /api/v1/products/42 (Cache Hit) ──► Edge Memory (&amp;lt;12ms, 0 PHP workers)
                          └── POST /api/v1/orders (Bypass) ─────────► Origin Nginx ──► PHP-FPM Worker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Advantages:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zero PHP Execution on Cache Hits:&lt;/strong&gt; Read queries return from edge memory without allocating a PHP-FPM worker or touching MySQL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SingleFlight Request Coalescing:&lt;/strong&gt; If 500 concurrent requests hit an expired cache key at the exact same millisecond, ApexCache forwards only 1 request to PHP-FPM. The remaining 499 requests wait and receive the fresh response directly from the edge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Global Proximity:&lt;/strong&gt; Responses are served from POPs close to the visitor, eliminating origin round-trip latency.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  4. Implementation in PHP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Return Cache and Tag Headers from PHP
&lt;/h3&gt;

&lt;p&gt;Configure your PHP application to return standard &lt;code&gt;Cache-Control&lt;/code&gt; headers along with custom cache tags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="c1"&gt;// Standard PHP or Framework Response&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Http\Controllers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Http\JsonResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;JsonResponse&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'variants'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'pricing'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;findOrFail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Cache-Control'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'public, max-age=86400, stale-while-revalidate=300'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'X-ApexCache-Tags'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"product:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, catalog, brand:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;brand_id&lt;/span&gt;&lt;span class="si"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Instant Cache Invalidation on Data Updates
&lt;/h3&gt;

&lt;p&gt;When an admin updates a product or price, invalidate the specific tag across all global edge nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="c1"&gt;// app/Services/CachePurgeService.php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CachePurgeService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$apiKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'https://api.getapexcache.com/api/v1/cache/invalidate'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$apiKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$apiKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Purge edge cache by tag in under 10ms.
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;purgeTags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$tags&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$ch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'tags'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$tags&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nb"&gt;curl_setopt_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="no"&gt;CURLOPT_POST&lt;/span&gt;           &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="no"&gt;CURLOPT_POSTFIELDS&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="no"&gt;CURLOPT_RETURNTRANSFER&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="no"&gt;CURLOPT_TIMEOUT&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="no"&gt;CURLOPT_HTTPHEADER&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'Authorization: Bearer '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$httpCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_getinfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLINFO_HTTP_CODE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;curl_close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$httpCode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trigger this service from your Eloquent model hooks, Symfony event subscribers, or doctrine lifecycle listeners:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In a Laravel Model Event or Observer:&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Product&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$purger&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;CachePurgeService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'services.apexcache.key'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nv"&gt;$purger&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;purgeTags&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;"product:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"catalog"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Automated Invalidation with Database CDC
&lt;/h2&gt;

&lt;p&gt;If you run background scripts, direct SQL imports, or microservices that modify the database directly without triggering PHP model events, you can connect ApexCache directly to your MySQL binlog or PostgreSQL WAL.&lt;/p&gt;

&lt;p&gt;ApexCache detects the committed row change in real time and automatically purges the corresponding cache tags in under 10ms. No application-level purge logic is required.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Production Benchmark: PHP-FPM Under Load
&lt;/h2&gt;

&lt;p&gt;We ran a load test against a standard PHP 8.3 + Laravel API deployed on an 8GB RAM, 4 vCPU server behind Nginx. We tested direct PHP-FPM execution versus PHP-FPM fronted by ApexCache:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Direct PHP-FPM (OPcache + Redis)&lt;/th&gt;
&lt;th&gt;With ApexCache Edge Proxy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;P50 Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;185ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;11ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;P99 Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1,420ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Max Concurrency Before 502s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;450 concurrent users&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;15,000+ concurrent users&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Origin CPU at 1,000 req/sec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;100% (Server crashed)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4% (Idle)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Active PHP-FPM Workers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;100/100 (Saturated)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2/100&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Error Rate (HTTP 502/504)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;14.8%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.00%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;p&gt;Scaling PHP does not require rewriting your backend in Go or migrating to complex microservices. &lt;/p&gt;

&lt;p&gt;By terminating cache lookups at an edge proxy and invalidating tags upon database commits, PHP-FPM workers only run when processing authentic data mutations. Your PHP application stays stable, responsive, and fast regardless of traffic spikes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Documentation &amp;amp; Setup:&lt;/strong&gt; &lt;a href="https://getapexcache.com/docs" rel="noopener noreferrer"&gt;getapexcache.com/docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SDKs &amp;amp; Integrations:&lt;/strong&gt; &lt;a href="https://getapexcache.com/docs/sdks" rel="noopener noreferrer"&gt;getapexcache.com/docs/sdks&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>php</category>
      <category>backend</category>
      <category>webdev</category>
      <category>performance</category>
    </item>
    <item>
      <title>How to Accelerate Next.js App Router API Routes by 10x</title>
      <dc:creator>Alok Deep</dc:creator>
      <pubDate>Sat, 29 Aug 2026 04:39:18 +0000</pubDate>
      <link>https://dev.to/alok1663/how-to-accelerate-nextjs-app-router-api-routes-by-10x-b01</link>
      <guid>https://dev.to/alok1663/how-to-accelerate-nextjs-app-router-api-routes-by-10x-b01</guid>
      <description>&lt;p&gt;Next.js App Router brought powerful server-side capabilities with React Server Components (RSC) and standard Route Handlers (&lt;code&gt;app/api/route.ts&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;However, in production deployments on Vercel or AWS Lambda, Next.js API Route Handlers frequently suffer from &lt;strong&gt;serverless execution overhead&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cold starts adding 150ms–400ms to initial invocations.&lt;/li&gt;
&lt;li&gt;Node.js runtime parsing and ORM database connection latency (100ms–300ms).&lt;/li&gt;
&lt;li&gt;High Vercel Fast Data Transfer and Serverless Function Execution unit bills under heavy traffic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a practical guide on how to edge-cache Next.js Route Handlers, bypass Node.js runtime overhead on read requests, and achieve sub-15ms API response times.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Bottleneck of Standard Route Handlers
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/api/products/route.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/lib/prisma&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Executes on every single HTTP request:&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Cold Lambda / Container boot&lt;/span&gt;
  &lt;span class="c1"&gt;// 2. TCP connection to PostgreSQL&lt;/span&gt;
  &lt;span class="c1"&gt;// 3. SQL execution and serialization&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;products&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;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;products&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;Under 1,000 requests per second, Vercel spins up dozens of concurrent serverless functions, exhausting database connection limits and resulting in high monthly invocation bills.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Fix: Edge Proxy Caching with SWR Headers
&lt;/h2&gt;

&lt;p&gt;Configure your Route Handler to return standard RFC 5861 &lt;code&gt;Cache-Control&lt;/code&gt; headers and route your API domain through an edge proxy (&lt;strong&gt;ApexCache&lt;/strong&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/api/products/route.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/lib/prisma&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&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;products&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;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&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="s1"&gt;Cache-Control&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="s1"&gt;public, max-age=86400, stale-while-revalidate=3600&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="s1"&gt;X-ApexCache-Tags&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="s1"&gt;products, catalog&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Instant Invalidation with Next.js Server Actions
&lt;/h2&gt;

&lt;p&gt;When an admin updates a product or a new order completes, trigger a background tag purge directly inside your Next.js Server Action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/actions/products.ts&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/lib/prisma&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;revalidateTag&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateProductStock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newStock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Update primary database&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newStock&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Purge edge proxy cache globally (&amp;lt;10ms)&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="s1"&gt;https://api.getapexcache.com/api/v1/cache/invalidate&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="s1"&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="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APEXCACHE_API_KEY&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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="s1"&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="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`product:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;productId&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;catalog&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="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Optional: Revalidate Next.js internal cache&lt;/span&gt;
  &lt;span class="nf"&gt;revalidateTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&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;h2&gt;
  
  
  Production Latency &amp;amp; Cost Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Next.js Serverless Direct&lt;/th&gt;
&lt;th&gt;With Edge Proxy (ApexCache)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;P50 API Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;240ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;11ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;P99 Latency (Cold Starts)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1,450ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;15ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Serverless Function Invocations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10,000,000&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;420,000 (95.8% reduction)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database Connection Pressure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (Connection exhaustion)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Minimal (Idle)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Monthly Vercel / Cloud Bills&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High Compute Charges&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Slashed by over 75%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;p&gt;Next.js Route Handlers are powerful for business logic, but forcing serverless functions to execute on every single read query degrades user latency and inflates cloud costs.&lt;/p&gt;

&lt;p&gt;By adding SWR headers and placing an intelligent edge proxy in front of your Next.js application, you serve 95%+ of API requests in under 15ms globally.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Documentation for Vercel &amp;amp; Next.js:&lt;/strong&gt; &lt;a href="https://getapexcache.com/docs/integrate-vercel" rel="noopener noreferrer"&gt;getapexcache.com/docs/integrate-vercel&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Redis vs. Memcached vs. Edge Caching: A 2026 Engineering Guide</title>
      <dc:creator>Alok Deep</dc:creator>
      <pubDate>Fri, 28 Aug 2026 06:33:20 +0000</pubDate>
      <link>https://dev.to/alok1663/redis-vs-memcached-vs-edge-caching-a-2026-engineering-guide-22f2</link>
      <guid>https://dev.to/alok1663/redis-vs-memcached-vs-edge-caching-a-2026-engineering-guide-22f2</guid>
      <description>&lt;p&gt;Over the past twenty-five years, caching architectures have evolved through three distinct generations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Generation 1 (Local In-Memory / Memcached):&lt;/strong&gt; Simple key-value byte caching on local servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generation 2 (In-VPC In-Memory Databases / Redis):&lt;/strong&gt; Centralized clusters with rich data structures, persistence, and pub/sub.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generation 3 (Distributed Edge Caching Proxies / ApexCache):&lt;/strong&gt; Globally distributed reverse proxies with Change Data Capture (CDC) and SingleFlight request coalescing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each tool was designed for specific trade-offs. Here is an architectural breakdown of when to use Memcached, when to use Redis, and when to use an Edge Caching Gateway.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Memcached: The Raw Multi-Threaded Byte Cache
&lt;/h2&gt;

&lt;p&gt;Memcached was created in 2003 for LiveJournal. It is a multi-threaded, in-memory key-value cache designed to store raw string or binary blobs.&lt;/p&gt;

&lt;h3&gt;
  
  
  When Memcached Wins:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Threaded Throughput:&lt;/strong&gt; Memcached utilizes multiple CPU cores efficiently on a single large instance (e.g. 64 vCPUs).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity:&lt;/strong&gt; If you only need a raw LRU string cache without persistence, data structures, or replication overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Where Memcached Falls Short:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;No native persistence or replication failovers.&lt;/li&gt;
&lt;li&gt;No rich data structures (lists, sets, sorted sets).&lt;/li&gt;
&lt;li&gt;In-VPC only: Does not reduce network distance for global users.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Redis: The In-Memory Data Structure Store
&lt;/h2&gt;

&lt;p&gt;Redis, released in 2009, transformed backend engineering by introducing rich data structures and replication.&lt;/p&gt;

&lt;h3&gt;
  
  
  When Redis Wins:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Queues &amp;amp; Background Jobs:&lt;/strong&gt; BullMQ, Sidekiq, Celery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-Time Pub/Sub &amp;amp; WebSockets:&lt;/strong&gt; Managing live event distribution across microservices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distributed Locks &amp;amp; Counters:&lt;/strong&gt; &lt;code&gt;INCRBY&lt;/code&gt;, &lt;code&gt;Redlock&lt;/code&gt;, atomic rate limiting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Geospatial &amp;amp; Leaderboards:&lt;/strong&gt; Sorted Sets (&lt;code&gt;ZSET&lt;/code&gt;) and Geo commands.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Where Redis Falls Short:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Response Caching:&lt;/strong&gt; Requires extensive custom invalidation code and suffers from dual-write desync.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single-Region Latency:&lt;/strong&gt; Sits in your cloud VPC, adding 150ms–300ms of cellular latency for global mobile users.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Edge Caching Gateways (ApexCache): The Global HTTP Proxy
&lt;/h2&gt;

&lt;p&gt;ApexCache sits directly in the HTTP traffic path between client applications and your backend servers.&lt;/p&gt;

&lt;h3&gt;
  
  
  When Edge Caching Wins:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Accelerating REST &amp;amp; GraphQL APIs:&lt;/strong&gt; Drops response times to under 15ms globally without modifying backend application code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Database Invalidation:&lt;/strong&gt; Uses Change Data Capture (CDC) to purge cache tags directly on database commits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thundering Herd Protection:&lt;/strong&gt; SingleFlight coalesces thousands of concurrent misses into 1 origin query.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Complete Technology Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Memcached&lt;/th&gt;
&lt;th&gt;Redis&lt;/th&gt;
&lt;th&gt;ApexCache (Edge Proxy)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Role&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;In-VPC Byte Cache&lt;/td&gt;
&lt;td&gt;In-VPC Data Structure Store&lt;/td&gt;
&lt;td&gt;Global API Edge Gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Network Location&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Central VPC Subnet&lt;/td&gt;
&lt;td&gt;Central VPC Subnet&lt;/td&gt;
&lt;td&gt;Globally Distributed Edge PoPs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Threading Model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-threaded&lt;/td&gt;
&lt;td&gt;Single-threaded event loop&lt;/td&gt;
&lt;td&gt;Multi-threaded Lock-Free Native&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Structures&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strings / Blobs only&lt;/td&gt;
&lt;td&gt;Strings, Hashes, Lists, Sets, ZSets&lt;/td&gt;
&lt;td&gt;Serialized HTTP JSON / GraphQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Invalidation Mechanism&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual app &lt;code&gt;delete()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Manual app &lt;code&gt;DEL&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Real-Time Database CDC &amp;amp; Webhooks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cache Stampede Guard&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Manual distributed locks&lt;/td&gt;
&lt;td&gt;Native SingleFlight coalescing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Custom client code&lt;/td&gt;
&lt;td&gt;Custom client code&lt;/td&gt;
&lt;td&gt;1 DNS CNAME record (Zero code)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Memcached&lt;/strong&gt; if you have a legacy, high-concurrency raw byte caching workload inside a single VPC.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Redis&lt;/strong&gt; for stateful operations: job queues, rate limiters, session storage, and pub/sub.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;strong&gt;ApexCache&lt;/strong&gt; in front of your HTTP APIs to handle read traffic, eliminate database stampedes, and deliver sub-15ms response times to global users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Documentation &amp;amp; Guides:&lt;/strong&gt; &lt;a href="https://getapexcache.com/docs" rel="noopener noreferrer"&gt;getapexcache.com/docs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>redis</category>
      <category>memcached</category>
      <category>database</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Why Your React Native FlatList Lags on Pagination (And How Edge SWR Fixes It)</title>
      <dc:creator>Alok Deep</dc:creator>
      <pubDate>Fri, 28 Aug 2026 04:24:09 +0000</pubDate>
      <link>https://dev.to/alok1663/why-your-react-native-flatlist-lags-on-pagination-and-how-edge-swr-fixes-it-2clb</link>
      <guid>https://dev.to/alok1663/why-your-react-native-flatlist-lags-on-pagination-and-how-edge-swr-fixes-it-2clb</guid>
      <description>&lt;p&gt;Every mobile developer has encountered this frustration when building infinite scroll feeds in React Native:&lt;/p&gt;

&lt;p&gt;The user scrolls down a product list or social feed. As they reach the bottom of page 1, &lt;code&gt;onEndReached&lt;/code&gt; triggers to fetch page 2. &lt;/p&gt;

&lt;p&gt;Over a mobile 4G or 5G connection:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The API request takes 450ms to 900ms to resolve.&lt;/li&gt;
&lt;li&gt;The user hits the bottom of the list and stares at a blank space or loading spinner.&lt;/li&gt;
&lt;li&gt;The scroll momentum stutters, and the feed feels sluggish.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is why client-side mobile pagination struggles on cellular networks, and how handling cursor pagination at the edge eliminates scroll lag.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Cellular Latency Problem in Mobile Apps
&lt;/h2&gt;

&lt;p&gt;Mobile devices do not have continuous high-speed fiber connections. Over cellular networks (4G/LTE/5G), establishing network handshakes and traversing physical distance to a central cloud server introduces high latency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mobile Phone (Cellular Tower)
         │  (Radio access network latency: 60ms–150ms)
         ▼
Regional Internet Routing (2-4 hops)
         │  (Transit RTT: 80ms–200ms)
         ▼
Origin Data Center (us-east-1)
         │
         ├── App Server JSON Serialization (40ms)
         └── Database Offset / Cursor Query (80ms)
         │
         ▼
Total Request Time: 260ms – 570ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a user scrolls quickly, the device cannot fetch pages fast enough to keep up with the scroll speed.&lt;/p&gt;




&lt;h2&gt;
  
  
  3 Fixes for Instant Mobile Pagination
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Cursor Normalization at the Edge
&lt;/h3&gt;

&lt;p&gt;Offset pagination (&lt;code&gt;?offset=40&amp;amp;limit=20&lt;/code&gt;) is slow on SQL databases because the database has to scan and discard 40 rows before returning the result.&lt;/p&gt;

&lt;p&gt;Use cursor-based pagination with indexed keys (&lt;code&gt;?cursor=item_9281&amp;amp;limit=20&lt;/code&gt;), and route the mobile app's API base URL through an edge proxy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// React Native API Client&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.your-app.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Routed via CNAME to ApexCache&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchFeedPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cursor&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;API_BASE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/v1/feed?cursor=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;limit=20`&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;API_BASE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/v1/feed?limit=20`&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;res&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="nx"&gt;url&lt;/span&gt;&lt;span class="p"&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="s1"&gt;Accept&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="s1"&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;
  
  
  2. Edge-Side Stale-While-Revalidate (SWR)
&lt;/h3&gt;

&lt;p&gt;Because page 1 and page 2 are requested frequently by all active users, the edge proxy caches the serialized JSON responses in memory at points of presence close to the user.&lt;/p&gt;

&lt;p&gt;When the mobile app requests &lt;code&gt;GET /v1/feed?cursor=item_9281&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The nearest edge node returns the cached JSON in &lt;strong&gt;under 14 milliseconds&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The network round-trip finishes before the user reaches the end of the current viewport.&lt;/li&gt;
&lt;li&gt;The FlatList renders the next 20 items seamlessly without any visible loading spinner.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Pre-Fetching Next Page During Scroll
&lt;/h3&gt;

&lt;p&gt;Combine edge caching with intelligent client-side prefetching. &lt;/p&gt;

&lt;p&gt;Instead of waiting for &lt;code&gt;onEndReached&lt;/code&gt; (which fires when the user is already at the bottom), trigger a background pre-fetch when the user scrolls past the 70% threshold:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useCallback&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FlatList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;View&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ActivityIndicator&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&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;ProductFeed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setItems&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;nextCursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setNextCursor&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;loadMore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;nextCursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;try&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;data&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;fetchFeedPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextCursor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="nf"&gt;setNextCursor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nextCursor&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="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextCursor&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FlatList&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;keyExtractor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;renderItem&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{({&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProductCard&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;      &lt;span class="nx"&gt;onEndReached&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loadMore&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;onEndReachedThreshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// Trigger early when half a screen away&lt;/span&gt;
      &lt;span class="nx"&gt;ListFooterComponent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ActivityIndicator&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;small&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Production Metrics
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Central Cloud Origin&lt;/th&gt;
&lt;th&gt;Edge Proxy (ApexCache)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;P50 Page Fetch Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;420ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;12ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;P99 Page Fetch Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1,280ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;18ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scroll Stutters / Janks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Frequent on fast scrolls&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Zero visible lag&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database Read IOPS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;100% load&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8% load (92% offload)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;p&gt;Mobile pagination lag is primarily a network distance and database scan problem.&lt;/p&gt;

&lt;p&gt;By adopting cursor-based queries and offloading paginated reads to a compiled edge proxy, mobile feeds load instantly across 4G and 5G connections without overloading your database.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Documentation on Mobile API Caching:&lt;/strong&gt; &lt;a href="https://getapexcache.com/docs/integrate-mobile" rel="noopener noreferrer"&gt;getapexcache.com/docs/integrate-mobile&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>reactnative</category>
      <category>mobile</category>
      <category>performance</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Stellate vs. ApexCache: Beyond GraphQL Caching</title>
      <dc:creator>Alok Deep</dc:creator>
      <pubDate>Fri, 28 Aug 2026 04:19:52 +0000</pubDate>
      <link>https://dev.to/alok1663/stellate-vs-apexcache-beyond-graphql-caching-140b</link>
      <guid>https://dev.to/alok1663/stellate-vs-apexcache-beyond-graphql-caching-140b</guid>
      <description>&lt;p&gt;GraphQL adoption has grown dramatically across modern web and mobile applications. As GraphQL backends scale, managing query complexity and resolver execution costs becomes a primary engineering challenge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stellate&lt;/strong&gt; (formerly GraphCDN) pioneered dedicated GraphQL edge caching, providing normalized caching and schema-aware invalidation for GraphQL endpoints.&lt;/p&gt;

&lt;p&gt;However, modern engineering backends are rarely pure GraphQL. In production, engineering stacks almost always operate a &lt;strong&gt;hybrid architecture&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GraphQL for complex frontend data trees&lt;/li&gt;
&lt;li&gt;REST endpoints for third-party integrations, mobile webhooks, and file uploads&lt;/li&gt;
&lt;li&gt;Internal microservices communicating via JSON over HTTP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is an architectural comparison between GraphQL-only caching proxies and unified edge gateways like &lt;strong&gt;ApexCache&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Unified REST + GraphQL Caching
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stellate:&lt;/strong&gt; Focuses exclusively on GraphQL APIs. If your stack includes REST endpoints (e.g. &lt;code&gt;/api/v1/auth&lt;/code&gt;, &lt;code&gt;/api/v1/webhooks&lt;/code&gt;, &lt;code&gt;/api/v1/products&lt;/code&gt;), you must maintain a separate caching and routing infrastructure for REST.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ApexCache:&lt;/strong&gt; Provides a &lt;strong&gt;unified edge gateway&lt;/strong&gt; supporting REST, GraphQL (AST body parsing &amp;amp; automatic query hashing), and microservice APIs under a single DNS hostname and control plane.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Invalidation: Mutation Sniffing vs. Real Database CDC
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In Stellate:&lt;/strong&gt; Invalidation primarily relies on intercepting GraphQL &lt;code&gt;mutation&lt;/code&gt; operations returned through the proxy, or dispatching manual purge requests via the Stellate Invalidation API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In ApexCache:&lt;/strong&gt; In addition to mutation detection and webhook purges, ApexCache connects directly to your database transaction log (PostgreSQL WAL, MySQL binlog, MongoDB Change Streams) via Change Data Capture (CDC). Direct database updates (cron jobs, admin scripts, direct SQL migrations) automatically invalidate cache tags across global edge nodes in &lt;strong&gt;under 10ms&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Enterprise In-VPC &amp;amp; Air-Gapped Deployment
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stellate:&lt;/strong&gt; Primarily hosted as a managed cloud SaaS platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ApexCache:&lt;/strong&gt; Offers both a global SaaS edge network and &lt;strong&gt;Enterprise In-VPC Self-Hosting&lt;/strong&gt;, allowing regulated healthcare, fintech, and government organizations to deploy air-gapped Docker proxy containers inside private AWS, GCP, or Azure VPCs.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Feature Comparison: Stellate vs. ApexCache
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Stellate&lt;/th&gt;
&lt;th&gt;ApexCache&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Protocol Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GraphQL only&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Unified REST, GraphQL &amp;amp; Microservices&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Request Coalescing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes (GraphQL)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes (SingleFlight across REST &amp;amp; GraphQL)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database Sync&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual Purge API &amp;amp; Mutations&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Real-Time Database CDC (PostgreSQL, MySQL, Mongo)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;In-VPC Self-Hosting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited / Enterprise Cloud&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Full Air-Gapped In-VPC Docker Deployment&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Developer Tier&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Usage-based tier&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100,000 requests/month free on Developer tier&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;p&gt;If your application architecture is 100% GraphQL and you only require managed cloud caching, Stellate provides a dedicated GraphQL solution.&lt;/p&gt;

&lt;p&gt;If your backend is a hybrid environment utilizing both REST and GraphQL, or if you require &lt;strong&gt;direct database CDC invalidation&lt;/strong&gt; and &lt;strong&gt;self-hosted In-VPC deployment options&lt;/strong&gt;, ApexCache provides a unified, higher-throughput edge infrastructure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Documentation &amp;amp; Setup:&lt;/strong&gt; &lt;a href="https://getapexcache.com/docs/vs-stellate" rel="noopener noreferrer"&gt;getapexcache.com/docs/vs-stellate&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>graphql</category>
      <category>api</category>
      <category>architecture</category>
      <category>performance</category>
    </item>
    <item>
      <title>Anatomy of a 1ms API Response: Breaking Down Network and Gateway Overheads</title>
      <dc:creator>Alok Deep</dc:creator>
      <pubDate>Fri, 28 Aug 2026 04:06:46 +0000</pubDate>
      <link>https://dev.to/alok1663/anatomy-of-a-1ms-api-response-breaking-down-network-and-gateway-overheads-pja</link>
      <guid>https://dev.to/alok1663/anatomy-of-a-1ms-api-response-breaking-down-network-and-gateway-overheads-pja</guid>
      <description>&lt;p&gt;What actually happens inside the operating system, network stack, and reverse proxy when an API returns a response in &lt;strong&gt;under 1 millisecond&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;When benchmarking low-latency systems, software overhead is often dwarfed by network and operating system mechanics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TCP handshake and TLS 1.3 key exchange&lt;/li&gt;
&lt;li&gt;Kernel socket buffer allocations and zero-copy transfers&lt;/li&gt;
&lt;li&gt;Memory cache lookup and hash table lock contention&lt;/li&gt;
&lt;li&gt;HTTP/2 and HTTP/3 multiplexing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is an engineering breakdown of where latency goes during an HTTP request, and how compiled native reverse proxies optimize every stage of the pipeline to deliver sub-millisecond gateway performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Request Lifecycle Timeline (Step-by-Step)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Time (µs)    Stage Description
0 µs         Client Socket sends HTTP GET request over existing TCP/TLS connection
120 µs       Kernel receives packets -&amp;gt; epoll/kqueue wakes up proxy event loop
220 µs       Zero-copy socket read into pre-allocated memory buffer
340 µs       HTTP parser parses headers &amp;amp; URI (SIMD-accelerated)
410 µs       Hasher computes aHash of cache key -&amp;gt; Look up in Sharded Cache Map
480 µs       Cache HIT -&amp;gt; Retrieve pointer to cached response byte slice
580 µs       Assemble HTTP response frame (HTTP/2 HEADERS + DATA frame)
720 µs       Write response bytes to kernel TCP socket buffer via writev / sendfile
850 µs       Packets leave network interface card (NIC)
Total Gateway Overhead: ~850 microseconds (0.85ms)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4 Engineering Techniques for Sub-Millisecond Gateways
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. SIMD-Accelerated HTTP Header Parsing
&lt;/h3&gt;

&lt;p&gt;Traditional string manipulation in standard HTTP parsers scans character-by-character.&lt;/p&gt;

&lt;p&gt;Modern native proxy engines use Single Instruction, Multiple Data (SIMD) CPU instructions (AVX2 on x86-64, NEON on ARM64) to parse 16 to 32 bytes of HTTP headers per CPU cycle. Validating header boundaries (&lt;code&gt;\r\n&lt;/code&gt;) and URI paths takes less than 80 nanoseconds.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Lock-Free Sharded In-Memory Caches
&lt;/h3&gt;

&lt;p&gt;If all worker threads read and write to a single global hash map protected by a mutex, CPU cores spend 40% of their cycles waiting for lock acquisition under 50,000 requests per second.&lt;/p&gt;

&lt;p&gt;ApexCache uses &lt;strong&gt;sharded concurrent hash tables&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keys are partitioned across 128 independent memory shards.&lt;/li&gt;
&lt;li&gt;Read operations use atomic reference counting (zero mutex locks).&lt;/li&gt;
&lt;li&gt;Multiple CPU cores read cached payloads simultaneously without thread contention.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Pre-Allocated Slab Memory Buffers (Zero GC Jitter)
&lt;/h3&gt;

&lt;p&gt;In garbage-collected languages (Go, Java, Node.js), allocating buffers for incoming requests creates memory churn that triggers garbage collection sweeps.&lt;/p&gt;

&lt;p&gt;Native compiled proxies pre-allocate slabs of fixed-size memory buffers at boot time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When a request arrives, a buffer is checked out from the pool.&lt;/li&gt;
&lt;li&gt;When the response is flushed to the socket, the buffer is recycled back into the pool.&lt;/li&gt;
&lt;li&gt;Memory usage remains flat, and garbage-collection pauses are completely eliminated.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Zero-Copy Socket Transfers with &lt;code&gt;writev&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Instead of copying response header strings and response body bytes into a new intermediate buffer before writing to the network socket, the proxy uses the &lt;code&gt;writev&lt;/code&gt; system call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Gathering write: passes memory pointers directly to kernel&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;iovec&lt;/span&gt; &lt;span class="n"&gt;iov&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;iov&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;iov_base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;header_bytes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;iov&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;iov_len&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;header_len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;iov&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="n"&gt;iov_base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cached_body_bytes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;iov&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="n"&gt;iov_len&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body_len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;writev&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iov&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The operating system reads the data directly from application memory into network packet buffers with zero intermediate copies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Benchmark Results: 100,000 Requests/Sec
&lt;/h2&gt;

&lt;p&gt;Here is a &lt;code&gt;wrk&lt;/code&gt; benchmark measuring gateway overhead on a 4-core AWS Graviton3 instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wrk &lt;span class="nt"&gt;-t4&lt;/span&gt; &lt;span class="nt"&gt;-c1000&lt;/span&gt; &lt;span class="nt"&gt;-d30s&lt;/span&gt; http://127.0.0.1:8080/api/v1/ping
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Latency Percentile&lt;/th&gt;
&lt;th&gt;Measured Proxy Overhead&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;P50 Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.24ms (240 microseconds)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;P90 Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.48ms (480 microseconds)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;P99 Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.82ms (820 microseconds)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Max Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.24ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Throughput&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;94,200 req/sec&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;p&gt;Sub-millisecond API response times are achieved by eliminating runtime garbage collection, avoiding lock contention on concurrent hash maps, and passing memory pointers directly to kernel network buffers.&lt;/p&gt;

&lt;p&gt;When gateway overhead is under 1ms, total API response time is determined solely by the speed of light to the user's nearest regional edge node.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Documentation &amp;amp; Benchmarks:&lt;/strong&gt; &lt;a href="https://getapexcache.com/docs" rel="noopener noreferrer"&gt;getapexcache.com/docs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>performance</category>
      <category>networking</category>
      <category>systems</category>
      <category>backend</category>
    </item>
    <item>
      <title>Building a Zero-Downtime Cache Invalidation Pipeline with Webhooks</title>
      <dc:creator>Alok Deep</dc:creator>
      <pubDate>Fri, 28 Aug 2026 04:00:57 +0000</pubDate>
      <link>https://dev.to/alok1663/building-a-zero-downtime-cache-invalidation-pipeline-with-webhooks-4m8d</link>
      <guid>https://dev.to/alok1663/building-a-zero-downtime-cache-invalidation-pipeline-with-webhooks-4m8d</guid>
      <description>&lt;p&gt;Cache invalidation is notoriously difficult to get right in high-throughput systems.&lt;/p&gt;

&lt;p&gt;If your cache TTL is too short (e.g. 10 seconds), your database gets overwhelmed by stampedes. If your TTL is too long (e.g. 24 hours), your application serves stale data after updates.&lt;/p&gt;

&lt;p&gt;The standard solution is &lt;strong&gt;event-driven cache invalidation&lt;/strong&gt;. Whenever a mutation occurs in your CMS, e-commerce admin, or custom backend, you trigger an invalidation webhook that purges matching cache entries across all edge points of presence.&lt;/p&gt;

&lt;p&gt;Here is how to design a resilient, zero-downtime cache invalidation pipeline with surrogate key tags, fanout workers, and retry guarantees.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Surrogate Key Tags (The Tagging Hierarchy)
&lt;/h2&gt;

&lt;p&gt;URL-based invalidation (&lt;code&gt;PURGE /api/products/linen-shirt&lt;/code&gt;) is brittle because a single product update might affect dozens of related endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The product details page (&lt;code&gt;/api/products/linen-shirt&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The category listing (&lt;code&gt;/api/collections/summer-apparel&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The brand page (&lt;code&gt;/api/brands/linen-co&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The search index and homepage featured carousel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of tracking every possible URL, attach &lt;strong&gt;surrogate key tags&lt;/strong&gt; to your HTTP responses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;
&lt;span class="na"&gt;X-ApexCache-Tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;product:1029, collection:summer, brand:42, catalog&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The edge proxy indexes these tags in an in-memory inverted index. When a purge request arrives for &lt;code&gt;product:1029&lt;/code&gt;, the proxy instantly evicts every cached response containing that tag across the cluster.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Invalidation Webhook Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Admin / CMS Mutation ──► Database Commit ──► Event Emitter (Webhook Dispatcher)
                                                   │
                                                   ▼
                                      ApexCache Invalidation API
                                                   │
                                    (Parallel Distributed Fanout)
                                                   ├── Edge PoP (US-East): Purged in 6ms
                                                   ├── Edge PoP (EU-West): Purged in 8ms
                                                   └── Edge PoP (AP-South): Purged in 9ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Implementation in Node.js / TypeScript
&lt;/h2&gt;

&lt;p&gt;Here is a resilient webhook dispatcher implementing non-blocking dispatch with exponential backoff retries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// services/cache-invalidator.ts&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;InvalidateOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;maxRetries&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;invalidateEdgeCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InvalidateOptions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;maxRetries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&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;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APEXCACHE_API_KEY&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;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.getapexcache.com/api/v1/cache/invalidate&lt;/span&gt;&lt;span class="dl"&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;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;maxRetries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&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;res&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="nx"&gt;endpoint&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="s1"&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="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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="s1"&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="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;payload&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="nx"&gt;res&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;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Purge successful&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;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[ApexCache] Invalidation returned status &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res&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;. Retrying...`&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;err&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="s2"&gt;`[ApexCache] Network error during purge attempt &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Exponential backoff: 50ms, 100ms, 200ms&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attempt&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="s1"&gt;[ApexCache] Failed to invalidate cache after max retries:&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="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Hooking Into ORMs (Prisma / Drizzle / TypeORM)
&lt;/h2&gt;

&lt;p&gt;Integrate the invalidation dispatcher directly into your database middleware or lifecycle hooks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Prisma Middleware Example&lt;/span&gt;
&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&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;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Check if the action was a mutation&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;create&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="s1"&gt;update&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="s1"&gt;delete&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="s1"&gt;updateMany&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&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="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Product&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="c1"&gt;// Fire-and-forget background eviction&lt;/span&gt;
      &lt;span class="nf"&gt;invalidateEdgeCache&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`product:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;productId&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;catalog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&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="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="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Latency and Consistency Guarantees
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purge Execution Time:&lt;/strong&gt; Tag evictions complete across global edge nodes in &lt;strong&gt;6ms to 12ms&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Downtime:&lt;/strong&gt; While tags are being evicted, the proxy handles in-flight requests seamlessly without returning 502/504 errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thundering Herd Shield:&lt;/strong&gt; The next incoming request for the evicted tag triggers a single coalesced origin fetch via SingleFlight, preventing database spikes.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Building a zero-downtime cache invalidation pipeline requires separating cache tags from raw URLs and dispatching event-driven purges on database commits.&lt;/p&gt;

&lt;p&gt;With tag-based surrogate keys and fast webhook dispatch, you can maintain long cache TTLs (24+ hours) while guaranteeing that updates reflect globally within milliseconds.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Documentation &amp;amp; API Reference:&lt;/strong&gt; &lt;a href="https://getapexcache.com/docs/invalidation" rel="noopener noreferrer"&gt;getapexcache.com/docs/invalidation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>backend</category>
      <category>architecture</category>
      <category>api</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
