How to Design a ThreadPool to Send 10 Million SMS in One Hour – Interview Insights

Sending 10 million marketing SMS within an hour forces careful thread‑pool design: avoid unbounded queues that cause OOM, choose a bounded ThreadPoolExecutor, dynamically tune core/max sizes, use CallerRunsPolicy for natural back‑pressure, and add persistence and compensation mechanisms to guarantee reliability.

Architecture Digest
Architecture Digest
Architecture Digest
How to Design a ThreadPool to Send 10 Million SMS in One Hour – Interview Insights

Introduction

A candidate was asked in an interview to design a thread pool that could send 10 million marketing SMS messages within one hour. The interviewer probed the candidate on core pool size, maximum size, and rejection policy, exposing common pitfalls.

Why Executors are a “no‑go” in production

Using Executors.newFixedThreadPool creates a LinkedBlockingQueue with Integer.MAX_VALUE capacity, which can cause OOM when processing tens of millions of tasks. newCachedThreadPool can create an unbounded number of threads, leading to CPU saturation.

Three practical levels of thread‑pool design

Level 1 – “golden formula” for initial sizing

First determine whether the workload is CPU‑bound or I/O‑bound; SMS sending is I/O‑bound. Apply the formula: core = CPU cores × target CPU utilization × (wait time ÷ compute time) . For massive I/O tasks start with a modest core count and adjust after load testing.

Level 2 – Dynamic tuning & full‑stack monitoring

Do not hard‑code coreSize, maxSize, or queueSize. Retrieve them from a configuration centre (e.g., Apollo, Nacos). Continuously monitor queue remaining capacity and pool activity; trigger alerts or auto‑scale when queue usage exceeds 80%.

Level 3 – Rejection policy as the ultimate safeguard

When the pool is saturated, choose CallerRunsPolicy (recommended) instead of the default AbortPolicy. This provides natural back‑pressure: the submitting thread (often the main thread) processes the task, slowing down further task production and preventing OOM.

In offline batch scenarios this back‑pressure becomes a powerful technique, whereas in online web services it would block request threads.

Reliability measures for crash scenarios

Persist task state (DB/Redis) before enqueuing.

Use an acknowledgment callback to mark completion.

Run a compensating scheduled job that rescans “in‑progress” tasks older than a threshold and retries them.

Interview answer template

Do not use Executors shortcuts because of unbounded queues. Set parameters based on the formula, select CallerRunsPolicy for back‑pressure, integrate a dynamic thread‑pool framework such as Hippo4J or DynamicTp for real‑time adjustments, and add persistence plus compensation for reliability.

Advanced thinking – distributed deployment

If a single machine cannot handle the load, split the workload across multiple nodes, ensure task sharding, define state hand‑over on node failure, and apply global rate‑limiting to avoid overwhelming the SMS gateway.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

high concurrencyJava concurrencyThreadPoolExecutorbackpressureOOM preventionthread pool tuning
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.