Scaling Under 1.07 Million Requests: How We Slashed CPU from 44% to 3% by Eliminating MySQL Metadata Locks and Absorbing 818K Bot Pings at Cloudflare Edge
On October 3, 2026, FamGateway's telemetry crossed a dramatic milestone: 1.07 million HTTP requests in a single 24-hour cycle, with 898,270 hits targeting our verification endpoint on our production Linux origin cluster. While memory remained cool at 6%, CPU utilization locked into a sustained 44% plateau. Here is the unvarnished engineering post-mortem of how we traced the bottleneck to hidden MySQL table metadata locks, dissected unthrottled merchant bot loops that generated 818,000 expired order pings, and deployed an edge-caching architecture that slashed CPU from 44% to 3% with zero payment latency.
How to scale high-throughput PHP APIs under millions of automated bot requests: 1) Eliminate all runtime DDL (ALTER TABLE) queries from application functions to prevent MySQL Table Metadata Locks. 2) Replace full-entity writes on rapid read endpoints with atomic, lockfile-throttled SQL counter updates. 3) Deploy a bifurcated Edge Caching strategy: emit Cache-Control: public, max-age=86400 on immutable terminal states (dead expired orders and completed payments) so Cloudflare serves cf-cache-status: HIT at the edge, while emitting Cache-Control: no-cache, private on active pending checkouts to guarantee real-time, zero-delay payment verification.
Purge DDL Locks
Delete runtime ALTER TABLE queries from user save functions to stop exclusive metadata locks from freezing concurrent threads.
Atomic Throttling
Update API key request counters with atomic single-row SQL queries throttled to 30s intervals, saving 95% of database disk writes.
Bifurcated TTLs
Cache dead orders (>2h old) and successful orders for 24h at the edge, while keeping active pending payments 100% uncached.
Cloudflare Edge HIT
Cloudflare Cache Rules absorb 818K rogue bot requests directly in Paris and Singapore, reducing origin server load by 80%.
1. The Problem: An Unexpected Traffic Surge to 1.07 Million Requests
When running a high-velocity non-custodial UPI gateway in India, platform reliability is paramount. Merchants connect automated Discord bots, Telegram stores, and e-commerce carts that continuously poll our verification API (/api/verify-order.php) to detect incoming UPI bank credits in real time.
FamGateway routinely handles massive transaction volume every single day (as documented in our September 2026 Traffic Transparency Report, where edge telemetry logged over 9.5 million requests in a month). However, on October 3, 2026, our monitoring dashboards detected an unprecedented traffic surge that spiked substantially higher than our expected daily average: over 1.07 million requests slammed our gateway in just 24 hours.
Because this incoming volume was far beyond our typical baseline, our proactive SRE anomaly detection flagged it immediately. At 03:30 AM IST, our origin cluster CPU utilization locked into a sustained 35% to 48% band (averaging 44%), while RAM usage was idling at just 6.1%. Because our monitoring spotted the anomaly within hours of the spike, we immediately initiated root-cause forensic analysis, isolated the bottleneck, and deployed zero-downtime architectural fixes on the spot.
22:11:04 up 47 days, 4:12, load average: 10.85, 10.32, 9.87
$ ps aux --sort=-%cpu | head -n 10
USER PID %CPU %MEM COMMAND
famgateway 1925844 3.7 0.1 php-fpm: pool www (/api/imap-processor.php)
famgateway 1927399 2.8 0.0 php-fpm: pool www (/api/verify-order.php)
famgateway 1926321 2.7 0.1 php-fpm: pool www (/api/verify-order.php)
famgateway 1926529 2.6 0.1 php-fpm: pool www (/api/imap-processor.php)
famgateway 1927472 2.5 0.0 php-fpm: pool www (/api/verify-order.php)
famgateway 1926216 2.4 0.1 php-fpm: pool www (/api/verify-order.php)
famgateway 1926226 2.2 0.1 php-fpm: pool www (/api/imap-processor.php)
# Over 20 simultaneous PHP worker processes competing for CPU resources
2. Forensic Investigation: Uncovering the MySQL Metadata Lock
To understand why simple status check queries were causing worker threads to pile up, we executed an immediate live inspection of active database threads using SHOW FULL PROCESSLIST:
Info: ALTER TABLE users ADD COLUMN use_custom_checkout TINYINT(1) DEFAULT 1
Id: 7727885 | Command: Query | State: Waiting for table metadata lock
Info: ALTER TABLE users ADD COLUMN brand_logo MEDIUMTEXT DEFAULT NULL
Id: 7727886 | Command: Query | State: Waiting for table metadata lock
Info: ALTER TABLE users ADD COLUMN reset_expires INT DEFAULT NULL
Id: 7727887 | Command: Query | State: setup
Info: ALTER TABLE users ADD COLUMN support_link VARCHAR(255) DEFAULT ''
The smoking gun was unmistakable: The database was choking on Table Metadata Locks. Why was our production server attempting to execute schema alterations on read requests?
The Anatomy of the Trap: Runtime Auto-Migrations
Examining includes/functions.php inside the saveUser() function revealed a dangerous relic from early prototype development:
function saveUser($userData) {
static $colsChecked = false;
if (!$colsChecked) {
$colsChecked = true;
try { getDB()->exec("ALTER TABLE users ADD COLUMN webhook_url TEXT DEFAULT ''"); } catch (\Exception $e) {}
try { getDB()->exec("ALTER TABLE users ADD COLUMN reset_token VARCHAR(255) DEFAULT NULL"); } catch (\Exception $e) {}
// ... 14 CONSECUTIVE ALTER TABLE QUERIES ON EVERY REQUEST!
}
// Full 40-column INSERT ... ON DUPLICATE KEY UPDATE query follows...
}
Because PHP processes are entirely stateless, every incoming HTTP request instantiates a new execution context where $colsChecked is initialized to false. When an automated bot pinged /api/verify-order.php, the endpoint called trackApiKeyUsage(), which invoked saveUser(). In turn, every single inbound bot request fired 14 DDL ALTER TABLE queries against MySQL.
Under MySQL InnoDB mechanics, an ALTER TABLE requires an exclusive Metadata Lock (MDL). When multiple concurrent requests attempt to acquire this lock:
- Request 1 grabs the exclusive metadata lock to check/alter the schema.
- Requests 2 through 15 are forced to queue in
Waiting for table metadata lockstate. - Crucially, subsequent simple
SELECTandUPDATEqueries on theuserstable are blocked behind the queued DDL operations. - PHP FastCGI processes cannot terminate because they are waiting on MySQL socket responses, causing worker pools to saturate and CPU to spike in thread spin-wait loops.
3. The Cloudflare Telemetry Revelation: How a Merchant's Telegram Bot Catalyzed Our Edge Architecture
While eliminating metadata locks resolved the database queueing, a fundamental architectural puzzle remained: Why was our infrastructure receiving over 1 million requests in 24 hours in the first place?
We pulled the 24-hour telemetry from our Cloudflare analytics dashboard, and the data uncovered an extraordinary pattern:
Total Requests: 1,070,000 (1.07M)
Endpoint Distribution: /api/verify-order.php → 898,270 (83.9%)
TOP STATUS CODES:
• HTTP 408 (Request Timeout / Expired): 818,520 (76.5% of total traffic)
• HTTP 200 (OK / Paid / Pending): 180,440 (16.8%)
• HTTP 401 (Unauthorized): 35,470 (3.3%)
TOP SOURCE CLIENTS & AUTOMATION AGENTS (ANONYMIZED):
1. MERCHANT-TELEGRAM-BOT/2.0 (IP: 51.75.xxx.xxx - European Cloud Cluster) → 362,560 requests
2. DIGITAL-STORE-BOT/2.0 (IP: 51.83.xxx.xxx - European Cloud Cluster) → 310,700 requests
3. STORE-FULFILLMENT-BOT/1.0 (IP: 51.75.xxx.xxx - European Cloud Cluster) → 87,540 requests
==============================================================
The Anatomy of the Merchant Telegram Bot Polling Bug
Out of 1.07 million total daily requests, over 818,000 requests were returning HTTP 408 Expired. Examining the database revealed that out of 19,874 total orders, 15,625 orders were abandoned checkouts that had expired hours or days prior.
Our top merchants run automated UPI Telegram shop bots and Discord storefronts powered by background Python and Node.js daemons running on dedicated cloud servers. When a customer initiates a purchase, the bot creates an order using their free FamPay API key and polls /api/verify-order.php every 1 to 3 seconds waiting for payment confirmation.
When inspecting the client scripts running inside these merchant bots, we identified a classic programmatic blind spot:
while True:
res = requests.get(f"https://famgateway.in/api/verify-order.php?api_key={KEY}&order_id={ORDER}")
data = res.json()
if data.get('status') == 'success':
deliver_digital_product()
break
# THE BUG: The bot only checked for 'success'!
# When a buyer abandoned payment and the order expired (HTTP 408 'expired'),
# the bot had NO exit condition! It kept polling verify-order.php every 1-3s FOREVER!
time.sleep(1)
Because the client bot lacked an exit clause for expired orders, every expired checkout session turned into a zombie thread on the merchant's server that continuously fired requests 24 hours a day, 7 days a week. With thousands of expired orders accumulating over time, these client daemons were pounding our backend with over 818,000 queries every single day.
The Two Paths: Blaming the Merchant vs. Leveling Up the Core Engine
When amateur payment gateways encounter a scenario like this, their first reflex is friction: they blame the developers, slap strict rate limits (HTTP 429 Too Many Requests), block merchant IP ranges, or send angry emails demanding: "Fix your buggy bot or get banned from our platform."
Aryan Gupta (Aryanispe) completely rejected that mindset. In world-class fintech engineering, Postel's Law is the golden standard: "Be conservative in what you send, be liberal in what you accept." Our merchants trust FamGateway to run their revenue streams silently and dependably. Telling busy business owners to halt their operations, debug code, and re-deploy dozens of Telegram bots across multiple cloud servers introduces unacceptable friction and merchant anxiety.
Instead of blaming our merchants, Aryan realized that their high-volume automated traffic was the best stress test possible. He dissected the problem down to first principles:
"When a customer's order expires, can it ever become active again? The clear answer is: No, never. And when an order is paid and marked as success, can it ever change? Again, No, never. So why are we hitting our origin MySQL database every 3 seconds for data that will never change again? Instead of asking merchants to fix their bots, we can make FamGateway completely invincible by caching immutable terminal states right at Cloudflare's Edge usingheader('Cache-Control: public, max-age=86400, s-maxage=86400')."
— Aryan Gupta, Founder & Chief Architect, FamGateway
That single realization turned a massive client polling storm into our greatest architectural triumph. Rather than demanding changes from our merchants, we leveled up our own engine — absorbing over 800,000 requests directly at the edge in 1 millisecond flat while dropping origin server CPU from 44% down to 3%.
4. The Solution: Two-Pronged Architecture Overhaul
Step 1: Removing Runtime DDL & Throttling API Key Counters
We completely purged the redundant ALTER TABLE statements from saveUser(), getAdminSettings(), and initWebhookJobsTable(). All columns had already been verified in production.
Next, we decoupled API key usage tracking from full user object rewrites. Instead of executing an expansive 40-column INSERT ... ON DUPLICATE KEY UPDATE on every 1-second ping, we engineered an atomic, lockfile-throttled query in includes/functions.php:
if (empty($apiKey)) return;
if (!$user) $user = getUserByApiKey($apiKey);
if (!$user || empty($user['id'])) return;
// 1. Maintain in-memory precision for real-time dashboard display
$user['total_requests'] = ((int)($user['total_requests'] ?? 0)) + 1;
// 2. High-performance throttling: only write to MySQL once every 30 seconds per API key
$throttleKey = sys_get_temp_dir() . '/famgw_key_' . md5($apiKey) . '.lock';
clearstatcache(true, $throttleKey);
$shouldUpdateDb = !file_exists($throttleKey) || (time() - filemtime($throttleKey)) >= 30;
if ($shouldUpdateDb) {
@touch($throttleKey);
try {
// Atomic update targeting only primary key — 0.5ms execution!
$stmt = getDB()->prepare("UPDATE users SET total_requests = :total, api_keys = :keys WHERE id = :id");
$stmt->execute([
'total' => $user['total_requests'],
'keys' => json_encode($user['api_keys'] ?? []),
'id' => $user['id']
]);
} catch (\Throwable $e) {}
}
}
Step 2: Bifurcated Edge Caching with Grace Window Preservation
The core architectural insight was simple: Once an order is paid or permanently dead, its status will NEVER change.
However, we had to account for a critical business requirement: FamGateway's 2-hour payment grace window. If a buyer pays an order 10 minutes after expiration, our IMAP worker still fulfills it. If we aggressively cached all expired orders for 24 hours, late payments would be masked by stale CDN responses.
We solved this by engineering a bifurcated Edge Cache policy in api/verify-order.php:
if ($order['status'] === 'success') {
header('Cache-Control: public, max-age=86400, s-maxage=86400');
jsonResponse(['status' => 'success', 'data' => $orderPayload]);
}
// 2. EXPIRED ORDERS: Differentiate between Dead vs Recent Grace Window
if (isset($order['expires_at_timestamp']) && time() > $order['expires_at_timestamp']) {
$createdAt = $order['created_at_timestamp'] ?? 0;
// If created >2 hours ago (past 7200s grace window): 100% Dead → Cache 24h at Edge
if ($createdAt > 0 && (time() - $createdAt) > 7200) {
header('Cache-Control: public, max-age=86400, s-maxage=86400');
} else {
// Within grace window: 10s micro-cache to tame 100ms bot loops while preserving late fulfillment
header('Cache-Control: public, max-age=10, s-maxage=10');
}
jsonResponse(['status' => 'expired', 'message' => 'Order expired'], 408);
}
// 3. PENDING ORDERS (Active live checkouts): Strictly Uncached!
header('Retry-After: 3');
header('Cache-Control: no-cache, private');
jsonResponse(['status' => 'pending', 'message' => 'Payment not received yet']);
Step 3: Activating Cloudflare Cache Rules
Because Cloudflare does not cache .php dynamic extensions by default, we configured a targeted Cloudflare Cache Rule:
- Rule Name:
Cache Verify Order - Matching Condition:
URI Path equals "/api/verify-order.php" - Cache Eligibility:
Eligible for cache - Edge TTL Setting:
Use cache-control header if present, bypass cache if not
5. Verification: Live Terminal Benchmarks & Cache Hits
We executed live terminal tests across all four operational scenarios to verify deterministic behavior:
Request 1: HTTP/2 408 | cf-cache-status: MISS (Cached at Edge)
Request 2: HTTP/2 408 | cf-cache-status: HIT (Delivered from Paris Edge in 1ms!)
=== SCENARIO 2: SUCCESS ORDER (Paid) ===
Request 1: HTTP/2 200 | cf-cache-status: MISS (Cached at Edge)
Request 2: HTTP/2 200 | cf-cache-status: HIT (Zero origin CPU consumed)
=== SCENARIO 3: LIVE PENDING ORDER (Real Buyer Paying) ===
Request 1: HTTP/2 200 | cf-cache-status: BYPASS (100% Real-Time Origin Check)
Request 2: HTTP/2 200 | cf-cache-status: BYPASS (Instant 3-second verification)
=== SCENARIO 4: UNAUTHORIZED REQUEST ===
Request 1: HTTP/2 401 | cf-cache-status: MISS (Protected)
6. The Results: Before vs. After
| Architecture Metric | Before Optimization | After Optimization | Net Engineering Gain |
|---|---|---|---|
| Origin Cluster CPU Usage | 44% – 48% (Plateau) | 3.2% – 4.0% | 90%+ Reduction |
| Active Worker Threads | 20+ FastCGI workers | 4 – 6 idle processes | 75% Thread Drop |
| MySQL Metadata Locks | Frequent (Spin-Wait) | 0 (Zero Locks) | 100% Lock Elimination |
| Unthrottled Merchant Bot Pings to Origin | 818,520 hits/day | ~0 hits/day (Edge HIT) | 80% Bandwidth Saved |
| Payment Verification Latency | 100ms – 200ms | 3ms – 5ms | 30x Faster Response |
7. Key Engineering Takeaways for Backend Developers
- Never Put DDL in Request Code: Schema adjustments like
ALTER TABLEbelong strictly in migration scripts. Running DDL inside web request handlers creates exclusive metadata locks that will take down any concurrent production database. - Audit Your 4xx Status Codes: High volumes of 4xx responses often conceal client-side polling bugs. Treat 404s and 408s as high-priority caching candidates if the resource is genuinely immutable.
- Decouple Reads from Writes: A status query (
GET /api/verify-order.php) should be a lightweight indexed read. Offload counter increments to atomic throttled updates to avoid disk I/O thrashing. - Bifurcate Cache TTLs for Real-Time Workflows: You don't have to choose between CDN caching and real-time payments. By using
no-cachefor pending orders andmax-age=86400for dead orders, you achieve instant edge performance without dropping a single payment.
8. Copycats Are Many, but Equals Are None: The Engineering Gap in FamPay Gateways
In the developer landscape and fintech automation space — whether compared to toy script clones or closed platforms (analyzed in our FamGateway vs ZapUPI architectural benchmark) — our copycat competitors are many, but equals are none.
Every few weeks, new copycats emerge across Telegram groups and low-effort forums, scraping our API endpoint signatures, attempting to clone our checkout pages, or selling slapdash PHP scripts that claim to be "automated FamPay gateways." But writing a toy script that verifies one test transaction on a localhost server is worlds apart from building an enterprise-grade fintech infrastructure that weathers real production load.
How I Handle FamGateway: Why Clones Collapse Under Fire
How I handle FamGateway under the hood — from asynchronous IMAP socket keep-alives and zero-lock atomic counter updates to intelligent bifurcated edge caching that absorbs over 1,000,000 requests in 24 hours on our production origin cluster — I don't think anyone else in the entire FamPay ecosystem can pull off.
When 100 concurrent buyers hit a competitor's cloned gateway, or when merchant automation loops fire rapid bursts, their servers choke: MySQL locks stall out, worker pools exhaust, and transactions get dropped. FamGateway, by contrast, absorbs over 800,000 automated status hits at the edge in 1 millisecond flat while keeping real origin CPU at a calm 3%.
The Telemetry Moat: Why New Competitors Will Only Burn Money
If a new competitor enters the market tomorrow with venture capital or deep pockets attempting to take down FamGateway, here is the harsh reality of why they will fail:
They have zero telemetry, zero error data, and zero battle scars.
A payment gateway engine cannot be perfected in an isolated test environment. What makes FamGateway invincible is our massive, living ecosystem of active merchants and automated bots that continuously hit our endpoints every second of the day. This immense volume gives us deep, real-world error data and edge-case telemetry that no textbook or AI prompt can simulate:
- We see how merchant daemons behave when network sockets glitch.
- We see how MySQL InnoDB behaves under concurrent table metadata locks.
- We see edge cases when banking notifications arrive delayed, out-of-order, or duplicated.
- We see how client-side polling loops behave when hundreds of buyers abandon orders simultaneously.
Crucially, every single fix — including the metadata lock elimination, atomic counter throttling, and bifurcated edge caching documented in this post-mortem — was figured out by myself (Aryan Gupta) through first-principles engineering and diagnostic profiling. No consultant suggested this. No competitor had ever documented it. We discovered it because we have the live data to see the invisible bottlenecks, and the engineering mind to craft deterministic solutions.
Without this live user base, without this repository of historical error data, and without the engineering depth to diagnose micro-bottlenecks, any new competitor trying to enter this space will simply burn through their money buying bigger servers, renting expensive VPS nodes, and rewriting code blindly — only to watch their engine crash the moment real merchants start driving serious volume.
The Giants vs. FamGateway: What We Lack in Banking APIs, We Surpass in Intellect
Let us be completely candid and grounded: we know we cannot compete with multi-billion-dollar titans like Razorpay, Cashfree, or PayU.
Those corporate giants have direct institutional banking integrations, private leased lines to NPCI, dedicated bank account relationship managers, and billions of dollars in VC backing (as we broke down in our Razorpay vs Cashfree vs FamGateway developer comparison). If an enterprise wants a conventional direct-banking gateway with standard corporate KYC and high MDR fees, Razorpay and Cashfree are the established industry standards.
FamGateway has none of that institutional privilege. We do not have direct core banking APIs. We do not have banking servers sitting in Mumbai data centers on private fiber. We built FamGateway from the ground up on raw public infrastructure.
Yet, in the specialized niche of FamPay UPI automation, no one can overthrow FamGateway. The complexity of this niche is unmatched.
Building an automated gateway on top of FamPay requires solving problems that traditional banking gateways never have to face:
- Maintaining persistent IMAP socket streams across flaky cloud connections without memory leaks.
- Parsing and validating cryptographic payment signatures from app notifications in sub-50 milliseconds.
- Handling rapid merchant polling loops without direct webhooks from the bank (which we solve via our 99.99% resilient webhook retry engine).
- Surviving over 1,000,000 daily API hits with ultra-efficient, single-digit CPU utilization.
Traditional multi-billion-dollar gateways will never touch this niche because it is too complex, too unconventional, and requires continuous reverse-engineering and socket-level tuning. And amateur script copycats will never conquer it because they lack the engineering intellect to solve problems when the rails break.
What FamGateway lacks in direct banking partnerships, we make up for with raw problem-solving capability, diagnostic clarity, and relentless engineering tenacity. We built this engine to be unbreakable, and every challenge we overcome only widens the gap between FamGateway and the rest of the world.
To every startup, freelancer, and indie developer seeking a free payment gateway without GST or current account: thank you for trusting FamGateway to power your business. We will continue innovating, scaling, and protecting your checkout flows with every line of code we write.
Explore More Developer & Merchant Guides
Master automated UPI integrations, merchant zero-GST setups, and FamPay API architecture:
- FamPay Payment Gateway for Merchants Guide (2026) — Complete walkthrough of instant merchant onboarding and automated UPI QR collections.
- Best Free Payment Gateway Without GST & Current Account — How creators and startups accept direct UPI payments without GST registration.
- Best ZapUPI Alternative in India: FamGateway vs ZapUPI — In-depth architectural comparison of latency, webhook reliability, and uptime.
- How to Get FamPay API Key and Accept UPI Payments — Step-by-step developer integration for Telegram bots, Discord carts, and websites.
- How to Verify FamPay UPI Payments Automatically — Sub-second webhook triggers and polling best practices.
- FamGateway Developer API Documentation — Interactive endpoints, SDK parameters, and HMAC signature validation.
Related Developer Guides & Resources
Scaling Zero-Fee UPI in India: FamGateway Handles 9.51M Requests & 1.85M IMAP Verifications | Sept 2026 Report
Official full-month September 2026 engineering transparency report for FamGateway (Sept 1 - Sept 30, comple...
Is FamGateway Safe? DPDP Act 2023 Compliance, AES-256 Encryption & Data Security Audit | India 2026
Complete 2026 security audit of FamGateway under India's DPDP Act 2023. Covers AES-256-GCM encryption, FIDO...
Aryan Gupta (Aryanispe): The Developer Who Built India's First FamPay UPI Gateway | Full Story 2026
The complete verified story of Aryan Gupta — the solo developer behind the handle Aryanispe — who engineere...