Your backend is slow… Not because of CPU. But because it's waiting. This is the difference between blocking and non-blocking I/O. Most backend APIs spend more time waiting than computing. Waiting for: • Database • External APIs • File system • Network calls 🔹 Blocking I/O When a thread makes a call (e.g., DB query), it waits until the result comes back. During that time: • The thread does nothing • It cannot handle another request • Memory is still allocated Under high traffic → thread pool fills → requests queue → latency increases. 🔹 Non-Blocking I/O The thread does not wait. Instead: • It registers a callback (or continuation) • It is freed to handle other requests • When the result is ready, execution resumes If your bottleneck is waiting, non-blocking can dramatically improve throughput. 🚨 Important Insight Non-blocking ≠ Faster. It means better resource utilization under high concurrency, not magic speed. One real gotcha: if you introduce a single blocking call inside an event-loop pipeline (e.g., a JDBC query), you block an event-loop thread and lose most of the benefit. Non-blocking has to be end-to-end. 🔹 So When Should You Use Which? Use Blocking I/O when: • Your app handles moderate concurrency • Your team values simplicity and debuggability • Your workload is CPU-heavy — non-blocking won't help here Use Non-Blocking I/O when: • You have high concurrency with lots of I/O waiting • You're building streaming or real-time data pipelines • Latency and throughput at scale are critical 🔥 Why This Matters Most scalability issues are not CPU problems. They're: • Thread exhaustion • Connection pool limits • Blocking external calls Understanding your I/O model is the first step toward designing scalable systems. #BackendEngineering #Java #SystemDesign #SoftwareArchitecture #Backend
Optimize Backend Performance: Blocking vs Non-Blocking I/O
More Relevant Posts
-
I've been writing async/await for years. I knew it "made things concurrent." But I never really knew *why* it worked. So I went down the rabbit hole. Threads, event loops, goroutines, race conditions — I tried to actually understand the mechanics, not just the syntax. Here's what I learned that surprised me the most: → In a typical API call, your server's CPU is idle "95%" of the time - just waiting for database responses and external API calls. Concurrency exists to stop that waste. → "Concurrency ≠ Parallelism." Concurrency is about 'dealing' with multiple things at once (even on one CPU core). Parallelism is about 'doing' multiple things at the same hardware moment. Most backend engineers confuse the two. → `async/await` is just syntactic sugar for callbacks. Every `await` transforms your function into a state machine that pauses, hands control back to the event loop, and resumes when the I/O is done. → Go's goroutines are not threads. A goroutine starts with ~2KB of stack. An OS thread starts with ~8MB. That's a 4000x difference - which is why Go can spin up a new goroutine 'per request' without exploding your server's memory. → Race conditions exist in single-threaded async code too. A `await` between a balance check and a balance update is enough to overdraw an account to -100 even with one thread. I wrote a detailed blog post covering all of this - OS thread mechanics, the event loop model, Go's M:N scheduler, shared memory, and race conditions with real diagrams and code. If you've ever wondered what's 'actually' happening under the hood when you type `await`, this is for you 👇 🖇https://lnkd.in/gwTrYTj6
To view or add a comment, sign in
-
-
The thread pool illusion Teams increase thread pool size when: ✔️ Latency increases ✔️ Requests queue up ✔️ CPU looks underutilized But they ignore: ❌ Blocking I/O ❌ Database locks ❌ External API latency ❌ Context switching overhead ❌ Memory pressure If threads are waiting, more threads = more waiting. --- 💥 Real production pattern API latency increased during traffic spike. Team increased thread pool: 50 → 200 → 500. Result? CPU context switching increased Memory usage spiked GC pauses increased DB connections exhausted Throughput didn’t improve Concurrency amplified inefficiency. --- 🧠 How senior engineers tune thread pools They ask first: ✔️ Is workload CPU-bound or I/O-bound? ✔️ What’s the average blocking time? ✔️ What is downstream capacity? ✔️ Are timeouts configured? ✔️ Is backpressure implemented? They measure: Throughput Queue length Execution time Rejection rate Threads are coordination tools — not performance multipliers. --- 🔑 Core lesson If you don’t understand what threads are waiting on, you don’t understand your system. Scaling concurrency without fixing bottlenecks just distributes failure faster. Efficiency beats parallelism. --- Subscribe to Satyverse for practical backend engineering 🚀 👉 https://lnkd.in/dizF7mmh If you want to learn backend development through real-world project implementations, follow me or DM me — I’ll personally guide you. 🚀 📘 https://satyamparmar.blog 🎯 https://lnkd.in/dgza_NMQ --- #BackendEngineering #Concurrency #SystemDesign #Performance #Java #DistributedSystems #Microservices #Scalability #Satyverse
To view or add a comment, sign in
-
-
The real bottleneck in modern applications is rarely the algorithm; it is the runtime’s ability to handle concurrency without hidden overhead. The latest changes in the .NET runtime highlight how deeply asynchronous execution shapes modern system behavior. The release of .NET 11 Preview 2 introduces further improvements to asynchronous runtime behavior, and this matters more than it first appears. In highly concurrent systems, performance is often not limited by CPU or memory, but by how efficiently the runtime schedules and resumes asynchronous work. Every unnecessary allocation, every inefficient continuation, and every poorly optimized task transition accumulates under load. This is why runtime-level optimizations for asynchronous execution are strategically important. They affect the entire software stack, from web APIs to distributed messaging systems. Developers often assume that “async” automatically scales, but the reality is more complex. Without efficient runtime support, asynchronous code simply shifts bottlenecks rather than removing them. Seen from this perspective, improvements in #DotNet runtimes are not minor technical refinements. They directly influence how scalable modern services become, especially in architectures built around #AsyncProgramming and message-driven systems. For teams building high-throughput services or complex #SoftwareArchitecture patterns, runtime behavior is no longer an implementation detail; it becomes a core performance factor. The broader implication is easy to overlook: the effectiveness of asynchronous programming depends less on syntax and far more on the runtime that executes it. Author & Publisher of this article: Dr. Holger Schwichtenberg
To view or add a comment, sign in
-
Stop blaming your backend for slow APIs. It’s not always your code. Most engineers jump straight into optimizing queries, reducing CPU usage, tweaking logic. But users are still complaining. Why? Because the real problem is often distance, not code. I call this the “Invisible Network Tax.” Your system is fast… until geography gets involved. Here’s what actually causes global latency: • Network distance dominates everything Requests traveling across continents add hundreds of milliseconds. No optimization beats physics. • No CDN = slow static delivery Serving images, JS, CSS from one region kills performance for global users. • Single-region deployments choke globally All traffic hitting one region = unnecessary delay for half your users. • Cross-region database calls Your API is fast, but DB calls across regions quietly add latency in the hot path. • No caching strategy Same data traveling thousands of miles again and again. Wasteful and slow. • Weak failure handling Timeouts, retries, packet loss → latency spikes you didn’t plan for. Most systems don’t fail because of bad code. They fail because they ignore geography. Takeaway: You can’t optimize latency without respecting distance. So tell me — Are you optimizing your code… or your system’s location? #Java #SpringBoot #SystemDesign #DistributedSystems #BackendEngineering #Scalability #PerformanceEngineering #MicroservicesArchitecture #SoftwareArchitecture #HighAvailability #LowLatency #APIPerformance #WebPerformance #SiteReliability #SRE #EngineeringLeadership #TechArchitecture #BackendDeveloper #FullStackDevelopment
To view or add a comment, sign in
-
13% of `clickhouse-connect` users run async. They account for 24% of all queries. Async users run harder workloads - and the old client was making them pay for it. Here's what changed in v0.12.0: 🔹 Native `aiohttp`-based client replaces the executor-wrapped sync client 🔹 The half-sync/half-async pattern bridges async I/O with synchronous parsing — without blocking the event loop 🔹 Network download and CPU parsing now overlap instead of taking turns 🔹 1.51× faster on mixed workloads at 32 concurrency; 1.16× geometric mean across all scenarios 🔹 Memory stays bounded at ~10MB buffer regardless of query result size
To view or add a comment, sign in
-
🚀 𝗪𝗵𝘆 𝗠𝗼𝘀𝘁 𝗕𝗮𝗰𝗸𝗲𝗻𝗱 𝗦𝘆𝘀𝘁𝗲𝗺𝘀 𝗙𝗮𝗶𝗹 𝗮𝘁 𝗦𝗰𝗮𝗹𝗲 (𝗔𝗻𝗱 𝗛𝗼𝘄 𝘁𝗼 𝗔𝘃𝗼𝗶𝗱 𝗜𝘁) After 7+ years in backend engineering, one pattern keeps repeating: 👉 Systems don’t usually fail because of traffic 👉 They fail because of bad design decisions early on Here are 3 mistakes I’ve seen again and again 👇 ❌ 𝟭. 𝗧𝗿𝗲𝗮𝘁𝗶𝗻𝗴 𝘁𝗵𝗲 𝗱𝗮𝘁𝗮𝗯𝗮𝘀𝗲 𝗹𝗶𝗸𝗲 𝗶𝗻𝗳𝗶𝗻𝗶𝘁𝗲 𝘀𝘁𝗼𝗿𝗮𝗴𝗲 Many systems keep piling data without archiving or partitioning. ✅ Fix: • Use proper indexing • Archive old data • Plan retention early ❌ 𝟮. 𝗜𝗴𝗻𝗼𝗿𝗶𝗻𝗴 𝗶𝗱𝗲𝗺𝗽𝗼𝘁𝗲𝗻𝗰𝘆 𝗶𝗻 𝗔𝗣𝗜𝘀 Duplicate requests WILL happen (retries, network issues, etc.) Without idempotency = duplicate records = data chaos. ✅ Fix: • Use unique request IDs • Design safe retry mechanisms • Make critical endpoints idempotent ❌ 𝟯. 𝗦𝗰𝗮𝗹𝗶𝗻𝗴 𝘃𝗲𝗿𝘁𝗶𝗰𝗮𝗹𝗹𝘆 𝗳𝗼𝗿 𝘁𝗼𝗼 𝗹𝗼𝗻𝗴 Throwing more CPU/RAM works… until it doesn’t. ✅ Fix: • Design stateless services • Use queues (RabbitMQ/Kafka) • Plan horizontal scaling early 💡 𝗥𝗲𝗮𝗹 𝘀𝗰𝗮𝗹𝗮𝗯𝗶𝗹𝗶𝘁𝘆 𝗶𝘀 𝗱𝗲𝘀𝗶𝗴𝗻𝗲𝗱 — 𝗻𝗼𝘁 𝗮𝗱𝗱𝗲𝗱 𝗹𝗮𝘁𝗲𝗿. If you're building backend systems today, think about scale from day one. What scaling mistake have you seen most often? 👇 Let’s discuss. #BackendEngineering #SystemDesign #Java #SpringBoot #Scalability #SoftwareEngineering #TechLeadership
To view or add a comment, sign in
-
-
🚨 𝗪𝗵𝗲𝗻 𝗘𝘃𝗲𝗿𝘆𝘁𝗵𝗶𝗻𝗴 𝗟𝗼𝗼𝗸𝗲𝗱 𝗙𝗶𝗻𝗲... 𝗕𝘂𝘁 𝗣𝗿𝗼𝗱𝘂𝗰𝘁𝗶𝗼𝗻 𝗪𝗮𝘀 𝗗𝗼𝘄𝗻 (𝗣𝗮𝗿𝘁 𝟭/𝟮) Last week, I encountered one of those incidents that every backend engineer dreads a production outage with no obvious signals. • No high CPU. • No memory spikes. • No crash loops. And yet… APIs were failing. At first glance, everything appeared normal. But clearly, something wasn’t right. So I started digging deeper beyond the usual dashboards. And that’s when I found it: 👉 Disk usage: 100% The entire server storage was full. 🔍 𝗥𝗼𝗼𝘁 𝗖𝗮𝘂𝘀𝗲 • It wasn’t traffic. • It wasn’t the database. • It was Docker logs. By default, Docker stores logs at: /var/lib/docker/containers/.../*.log And the critical oversight: ➡️ Logs do not rotate automatically unless explicitly configured. One high-traffic service with verbose logging kept writing continuously: • No limits • No rotation • No alerts Until the disk was completely exhausted. 💥 𝗜𝗺𝗽𝗮𝗰𝘁 • Server became unstable • New logs could not be written • APIs started failing silently A classic case of: “Everything works… until it doesn’t.” 📌 𝗟𝗲𝘀𝘀𝗼𝗻 Silent failures are the most dangerous because they hide in plain sight. 👉 In Part 2, I’ll share how I resolved this issue and ensured it doesn’t happen again. 🔔 Follow me for real-world backend engineering lessons, production incidents, and practical debugging insights. #BackendEngineering #DevOps #Docker #ProductionIssues #SystemDesign #SoftwareEngineering #Debugging #CloudComputing #Microservices #NodeJS #EngineeringLessons
To view or add a comment, sign in
-
-
Core Distributed System Concepts every backend developer should know while implementing them in real world projects .🌍 🧑💻 1. Scalability (Stateful/Stateless,Horizontal/Vertical) 2.Consistency (Strong and eventual ) 3. CAP theorem 4.Consensus Algorithms 5.Replication 6.Sharding/Partitioning 7.Load Balancing 8. Distributed Transactions ( Two-Phase Commit , Saga Pattern ) 9.Fault Tolerance (Circuit breaker,retries) 10.Observability (Logging/Metrics) 11.Distributed Caching 12.Message Queues/ Event - Driven Systems 13.Idempotency (Retry logic,Distributed APIs) 14.Resilent (Rate Limiting )
To view or add a comment, sign in
-
Async in .NET just got a serious upgrade. • No refactor. • No rewrite. • No new syntax. Just faster execution and lower memory usage. For years, every async/await call generated a compiler state machine behind the scenes. Now? More of that work moves into the runtime. That means: • Less allocation • Less GC pressure • Better performance under load • Cleaner async behavior And your code doesn’t change. These are the best kinds of optimizations — the ones that improve every async call automatically. If you're building high-throughput APIs or distributed systems, this matters more than you think. Are you tracking async performance in your services? #dotnet #csharp #async #performance #backend #softwareengineering
To view or add a comment, sign in
-
-
🧠 The Memory Leak That Took Days to Notice Everything looked fine. The Node.js service was running smoothly: • CPU usage was normal • Requests were fast • No obvious errors in logs But after a few days, something strange started happening. The server became slower. Memory usage kept increasing. Eventually… the process crashed. This wasn’t a sudden failure. It was a memory leak. What a Memory Leak Looks Like in Node.js In Node.js, memory usage often grows in the heap. Normally: • Objects are created • Used for a request • Garbage collected later But when references to objects are never released, The garbage collector can’t free that memory. Over time, the heap keeps growing. One Common Cause: Event Listeners A frequent mistake happens with event listeners. For example, listeners added repeatedly. emitter.on('data', handler) If those listeners aren’t removed when they’re no longer needed: • They keep references to objects • Those objects stay in memory • Memory slowly accumulates Node.js even warns about this with: MaxListenersExceededWarning But it’s easy to ignore. Why These Bugs Are Hard to Detect Memory leaks rarely break things immediately. Instead, they appear as: • Gradually increasing memory usage • Slower garbage collection • Performance degradation over time • Crashes after hours or days In development, everything seems fine. Production traffic exposes the problem. How Developers Usually Detect It Memory leaks often become visible through: • Monitoring heap usage • Profiling memory snapshots • Checking event listener counts • Watching long-running processes Tools like heap snapshots and profiling help identify objects that never get released. 🎯 Final Lesson Not every bug appears instantly. Some only show up after hours of traffic and thousands of requests. Memory leaks are dangerous because they grow quietly — until the system finally runs out of memory. #NodeJS #BackendDevelopment #MemoryLeak #JavaScript #SoftwareEngineering #Debugging
To view or add a comment, sign in
-
Explore content categories
- Career
- Productivity
- Finance
- Soft Skills & Emotional Intelligence
- Project Management
- Education
- Technology
- Leadership
- Ecommerce
- User Experience
- Recruitment & HR
- Customer Experience
- Real Estate
- Marketing
- Sales
- Retail & Merchandising
- Science
- Supply Chain Management
- Future Of Work
- Consulting
- Writing
- Economics
- Artificial Intelligence
- Employee Experience
- Workplace Trends
- Fundraising
- Networking
- Corporate Social Responsibility
- Negotiation
- Communication
- Engineering
- Hospitality & Tourism
- Business Strategy
- Change Management
- Organizational Culture
- Design
- Innovation
- Event Planning
- Training & Development