惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Schneier on Security
Schneier on Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Tenable Blog
P
Proofpoint News Feed
V
Vulnerabilities – Threatpost
Project Zero
Project Zero
Latest news
Latest news
S
Schneier on Security
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Threatpost
Simon Willison's Weblog
Simon Willison's Weblog
Cyberwarzone
Cyberwarzone
T
The Exploit Database - CXSecurity.com
C
CERT Recently Published Vulnerability Notes
Spread Privacy
Spread Privacy
Cisco Talos Blog
Cisco Talos Blog
T
Troy Hunt's Blog
S
Secure Thoughts
C
Cisco Blogs
Application and Cybersecurity Blog
Application and Cybersecurity Blog
V2EX - 技术
V2EX - 技术
Hacker News: Ask HN
Hacker News: Ask HN
O
OpenAI News
L
LINUX DO - 最新话题
T
Threat Research - Cisco Blogs
Recent Commits to openclaw:main
Recent Commits to openclaw:main
P
Palo Alto Networks Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
博客园_首页
月光博客
月光博客
博客园 - 【当耐特】
雷峰网
雷峰网
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园 - 叶小钗
aimingoo的专栏
aimingoo的专栏
L
Lohrmann on Cybersecurity
D
DataBreaches.Net
美团技术团队
B
Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
有赞技术团队
有赞技术团队
D
Docker
Jina AI
Jina AI
The GitHub Blog
The GitHub Blog
H
Hacker News: Front Page
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
AI
AI
Martin Fowler
Martin Fowler
Attack and Defense Labs
Attack and Defense Labs
小众软件
小众软件

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Why setTimeout is Lying to Your Retry Logic
Arnav Gupta · 2026-06-18 · via DEV Community

Arnav Gupta

You've written retry logic. It probably looks something like this:

async function withRetry(fn, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      return await fn();
    } catch (err) {
      if (i === retries - 1) throw err;
      await new Promise(r => setTimeout(r, 200 * (i + 1)));
    }
  }
}

You test it locally. You simulate a slow dependency like this:

const fakeDB = async () => {
  await new Promise(r => setTimeout(r, 200)); // simulate DB
  return { id: 1, name: 'test' };
};

Your retry logic works. Tests pass. You ship it.

Then in production, your app starts dropping requests under load.

The problem isn't your retry logic. It's your fake.


Real dependencies don't have flat latency

Here's what your Postgres instance actually looks like in production:

  • p50: 5ms — half of all queries finish in under 5ms
  • p95: 50ms — 95% finish under 50ms
  • p99: 200ms — 99% finish under 200ms
  • p99.9: 2000ms — that one unlucky query during a GC pause

Your setTimeout(fn, 200) simulates the worst case, every single time. That's not how production works. And because it's not how production works, your retry logic has never actually been tested against reality.

The bugs hide in the variance — not in the slow case, but in the unpredictability.


What the real distribution looks like

Latency in distributed systems follows a lognormal distribution. It's right-skewed: most requests are fast, a meaningful minority are slow, and a small tail is very slow.

This shape comes from how real systems work:

  • GC pauses — Java, Go, and even Node's garbage collector occasionally stops the world
  • Cold caches — first query after a cache miss is always slower
  • Network jitter — packet routing isn't deterministic
  • Noisy neighbors — other workloads on the same hardware compete for resources
  • Connection pool exhaustion — when all connections are busy, new queries wait

None of these are constant. They're random, rare, and multiplicative — which is exactly what produces a lognormal shape.


Why this matters for retry logic specifically

Consider this scenario: your p99 latency is 200ms and your timeout is 250ms.

With setTimeout(fn, 200), every test call takes exactly 200ms — safely under your timeout. Tests pass.

In production, the lognormal tail means 0.1% of calls take 500ms or more. Your 250ms timeout fires, your retry triggers, and now you're sending the same request again to an already-stressed database. Under load, this cascades.

This is the exact failure mode that causes retry storms — and it only appears in production because your local tests used flat delays.

The bugs that flat delays hide:

  • Timeouts that are too tight for the real p99
  • Retry logic that amplifies load instead of handling it gracefully
  • Circuit breakers that never open during tests but open constantly in production
  • Backoff strategies that feel correct locally but collapse under real variance

The fix: simulate real latency distributions

Instead of a flat delay, fit a lognormal distribution to real p50/p99 values and sample from it. Every call gets a different delay — most are fast, some are slow, a few are very slow. Just like production.

Here's the math:

function fitLognormal(p50, p99) {
  // p50 = median = e^mu  →  mu = ln(p50)
  // p99 = e^(mu + 2.326*sigma)
  const mu = Math.log(p50);
  const sigma = (Math.log(p99) - mu) / 2.326;
  return { mu, sigma };
}

function sampleLatency(p50, p99) {
  const { mu, sigma } = fitLognormal(p50, p99);
  // Box-Muller transform
  const u1 = Math.random(), u2 = Math.random();
  const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
  return Math.exp(mu + sigma * z);
}

Call sampleLatency(5, 200) ten times and you'll get something like:

3ms, 7ms, 2ms, 12ms, 4ms, 180ms, 6ms, 3ms, 9ms, 440ms

That's what your database actually looks like.


Using slowdep

I built slowdep to make this a one-liner. It wraps any async function with a lognormal latency profile — either a built-in preset or your own p50/p99 values.

npm install slowdep

import { withLatency } from 'slowdep';

// before: flat fake
const fakeDB = async (id) => ({ id, name: 'test' });

// after: realistic latency
const fakeDB = withLatency(async (id) => ({ id, name: 'test' }), 'postgres');

Now run your retry logic against it:

const result = await withRetry(() => fakeDB(42));

You'll immediately see things you didn't see before:

  • Some retries succeed on the second attempt (realistic)
  • Occasional calls hit your timeout (revealing tight timeouts)
  • Rare calls cascade into all retries failing (revealing missing backoff jitter)

Built-in presets cover the most common dependencies:

Preset p50 p99 Error rate
'postgres' 5ms 200ms 0.1%
'redis' 1ms 20ms 0.05%
'stripe' 200ms 2000ms 0.2%
'openai' 800ms 8000ms 0.5%
's3' 30ms 500ms 0.1%

You can also pass custom profiles:

const slowFetch = withLatency(fetchAPI, {
  p50: 100,
  p99: 3000,
  errorRate: 0.02, // 2% transient errors
});


The real test

Here's what testing retry logic actually looks like with realistic latency:

import { withLatency } from 'slowdep';

// realistic postgres simulation
const db = withLatency(async (id) => {
  return { id, name: 'Arnav' };
}, 'postgres');

// your retry logic
async function withRetry(fn, retries = 3, baseDelay = 100) {
  for (let i = 0; i < retries; i++) {
    try {
      return await fn();
    } catch (err) {
      if (i === retries - 1) throw err;
      // exponential backoff with jitter
      const delay = baseDelay * Math.pow(2, i) * (0.5 + Math.random() * 0.5);
      await new Promise(r => setTimeout(r, delay));
    }
  }
}

// now you're actually testing against production-like behavior
const result = await withRetry(() => db.findUser(42));

Run this a hundred times. Watch which calls fail. Tune your timeouts and backoff based on what you see. That's actual resilience testing — not false confidence from a flat 200ms.


Summary

  • Real dependency latency is lognormal: fast most of the time, occasionally slow, rarely very slow
  • setTimeout(fn, 200) tests only the worst case, every time — it hides the bugs that only appear from variance
  • Fitting a lognormal distribution to your p50/p99 values gives you realistic simulation in one function call
  • slowdep wraps any async function with zero dependencies and built-in presets for postgres, redis, stripe, openai, s3, and more

If your retry logic has never been tested against real latency variance, it probably has bugs you haven't found yet.


Source code and presets: github.com/arnnnavvvvv/slowdep