Batching Strategy Tối Ưu: Nhóm API Requests (100 Email/Lần) Giảm Execution Count, Tiết Kiệm Chi Phí

Tóm tắt nội dung chính
Batching Strategy là cách nhóm các yêu cầu API lại với nhau (ví dụ: gửi 100 email trong một lần gọi) để giảm số lần thực thi, tiết kiệm chi phí và tăng hiệu suất.
– Bài viết sẽ khai thác vấn đề thực tế, giải pháp tổng quan, hướng dẫn chi tiết từng bước, template quy trình, các lỗi phổ biến, cách scale, chi phí thực tế, số liệu trước‑sau, FAQ và cuối cùng là hành động bạn có thể thực hiện ngay.


1️⃣ Vấn đề thật mà mình và khách hay gặp mỗi ngày

1️⃣ Giới hạn rate‑limit – Nhiều dịch vụ email (SendGrid, Mailgun) hoặc SMS chỉ cho phép tối đa 10 request/giây. Khi gửi hàng nghìn email trong một đợt, chúng ta nhanh chóng bị chặn và phải retry, gây trễ và tốn thời gian.

2️⃣ Chi phí tính theo request – Các nhà cung cấp API tính phí dựa trên số lần gọi (ví dụ: $0.0005 /request). Gửi 10 000 email với 1 request/email sẽ tốn $5, trong khi nếu batch 100 email/trong một request thì chỉ còn $0.05.

3️⃣ Quản lý lỗi phức tạp – Khi mỗi email là một request riêng, việc log, retry và báo cáo lỗi trở nên rối rắm; nếu một request batch thất bại, chúng ta phải phân tích xem email nào trong batch bị lỗi.

⚡ Best Practice: Trước khi thiết kế workflow, luôn xác định “điểm nghẽn” (rate‑limit, chi phí) và mục tiêu giảm số request tối thiểu 70 % so với cách naïve.


2️⃣ Giải pháp tổng quan (text art)

┌─────────────────────┐          ┌─────────────────────┐
│   Nguồn dữ liệu      │          │   API Provider      │
│   (DB / Queue)      │          │   (Email / SMS)     │
└───────┬─────────────┘          └───────┬─────────────┘
        │                               │
        ▼                               ▼
   ┌───────────────┐          ┌───────────────────┐
   │ Batch Builder │ ───────► │ Batch API Call    │
   └───────┬───────┘          └───────┬───────────┘
           │                          │
           ▼                          ▼
   ┌───────────────┐          ┌───────────────────┐
   │ Retry / Split │ ◄─────── │ Response Handler  │
   └───────────────┘          └───────────────────┘
  • Batch Builder: Thu thập các payload (email) từ queue, gom thành một mảng JSON (batchSize = 100).
  • Batch API Call: Gửi một HTTP POST duy nhất tới endpoint /sendBatch.
  • Response Handler: Kiểm tra kết quả từng mục trong batch; nếu có lỗi (status != 200) thì split ra các request nhỏ hơn để retry.

3️⃣ Hướng dẫn chi tiết từng bước

Bước 1 – Xác định kích thước batch tối ưu

  • Kiểm tra tài liệu API: thường có giới hạn maxBatchSize (ví dụ: 500).
  • Thực nghiệm với các kích thước 50, 100, 200 để đo latency và tỷ lệ lỗi.

Bước 2 – Thiết kế schema batch

{
  "batchId": "20251207_001",
  "emails": [
    {"to":"[email protected]","subject":"Chào bạn","body":"..."},
    {"to":"[email protected]","subject":"Chào bạn","body":"..."}
    // … up to batchSize
  ]
}

Bước 3 – Cài đặt Batch Builder (Node.js ví dụ)

const BATCH_SIZE = 100;
let buffer = [];

function enqueue(email) {
  buffer.push(email);
  if (buffer.length >= BATCH_SIZE) {
    flush();
  }
}

async function flush() {
  const payload = { batchId: generateId(), emails: buffer };
  buffer = []; // reset ngay trước khi gửi để tránh mất mát khi retry

  try {
    const res = await fetch('https://api.mailprovider.com/sendBatch', {
      method: 'POST',
      headers: {'Content-Type':'application/json'},
      body: JSON.stringify(payload)
    });
    const result = await res.json();
    handleResponse(result);
  } catch (err) {
    // 🐛 Retry toàn bộ batch sau delay exponential
    setTimeout(() => flush(), getBackoff());
  }
}

Bước 4 – Response Handler & Retry Logic

function handleResponse(result) {
  const failed = result.emails.filter(e => e.status !== 'sent');
  if (failed.length === 0) return;

  // Nếu số lỗi <10% → split thành các batch nhỏ hơn để retry
  const chunkSize = Math.max(1, Math.floor(failed.length / 2));
  for (let i = 0; i < failed.length; i += chunkSize) {
    const subBatch = failed.slice(i, i + chunkSize);
    retrySubBatch(subBatch);
  }
}

Bước 5 – Đặt lịch flush cuối ngày (nếu buffer chưa đầy)

setInterval(() => {
  if (buffer.length > 0) flush();
}, 5 * 60 * 1000); // mỗi 5 phút một lần

4️⃣ Template quy trình tham khảo

Bước Mô tả Công cụ / Code mẫu
Thu thập Đọc dữ liệu từ DB/Queue SELECT * FROM email_queue WHERE status='pending'
Gom batch Push vào mảng tới BATCH_SIZE buffer.push(item)
Gửi POST /sendBatch fetch(... )
Kiểm tra Đọc result.emails[i].status if(status!=='sent')
Retry Split & retry retrySubBatch()
Log Ghi log chi tiết winston.info(...)
Cleanup Đánh dấu đã gửi UPDATE email_queue SET status='sent' WHERE id IN (...)

🛡️ Lưu ý: Mọi thay đổi trạng thái DB nên được thực hiện trong transaction để tránh “duplicate send”.


5️⃣ Những lỗi phổ biến & cách sửa

Lỗi Nguyên nhân Cách khắc phục
429 Too Many Requests Batch size vượt quá quota rate‑limit của provider Giảm BATCH_SIZE, bật exponential backoff
Partial Success (một số email trả về error) Địa chỉ email không hợp lệ hoặc nội dung spam‑triggered Tách các email lỗi ra batch riêng, lưu log để clean danh sách
Timeout khi gửi batch lớn Kết nối mạng chậm hoặc server provider quá tải Thêm timeout (fetch(...,{timeout:30000})) và chia batch thành các phần nhỏ hơn
Duplicate Send Buffer không được reset sau khi retry thất bại Đảm bảo buffer = [] luôn được thực hiện trước khi gọi API

⚡ Tip: Sử dụng circuit‑breaker pattern để ngắt kết nối khi tỷ lệ lỗi > 20 % trong vòng N request.


6️⃣ Khi muốn scale lớn thì làm sao

1️⃣ Phân tán queue – Dùng Kafka hoặc RabbitMQ với partition theo “domain” (ví dụ: region). Mỗi consumer sẽ có một Batch Builder riêng, giúp tăng throughput lên hàng nghìn batch/giờ.

2️⃣ Horizontal scaling – Deploy container Docker cho service Batch Builder; sử dụng Kubernetes HPA (Horizontal Pod Autoscaler) dựa trên metric queue_length.

3️⃣ Cache batchId – Để tránh gửi trùng lặp khi pod restart, lưu batchId đã gửi vào Redis với TTL = 24h.

4️⃣ Multi‑provider fallback – Khi provider A trả về lỗi rate‑limit liên tục (> 5 lần), tự động chuyển sang provider B (Mailgun ↔ SendGrid).

🛡️ Bảo mật: Mã hoá key API bằng KMS; không lưu key trong mã nguồn.


7️⃣ Chi phí thực tế

Giả sử:

  • Provider tính $0.0005 /request.
  • Gửi mỗi ngày 10 000 email.

Trường hợp không batching

  • Số request = 10 000 → Chi phí = $5/ngày → $150/tháng.

Trường hợp batch size = 100

  • Số request = 10 000 / 100 = 100 → Chi phí = $0.05/ngày → $1.5/tháng.

Công thức tính chi phí

Chi phí hàng tháng = (Số request mỗi ngày × Giá mỗi request × Số ngày trong tháng)

ROI = ((Chi phí cũ – Chi phí mới) / Chi phí cũ) × 100%

\huge ROI=\frac{Cost_{old}-Cost_{new}}{Cost_{old}}\times100

Giải thích tiếng Việt: ROI ở đây là tỷ lệ phần trăm giảm chi phí so với cách không batching.

Áp dụng:

  • Cost_old = $150
  • Cost_new = $1.5

ROI = ((150 – 1.5)/150) × 100% ≈ 99% giảm chi phí.


8️⃣ Số liệu trước – sau

Chỉ số Trước batching Sau batching
Requests/ngày 10 000 ~100
Thời gian trung bình/req (ms) ~120 ~180 (do payload lớn hơn)
Tổng latency gửi mail (s) ~1 200 ~180
Chi phí tháng ($) $150 $1.5
Tỷ lệ lỗi (% requests) 2.3% <0.5%

Nhận xét: Mặc dù thời gian mỗi request tăng nhẹ vì payload lớn hơn, tổng thời gian hoàn thành giảm tới ~85 % và chi phí giảm gần như toàn bộ.


9️⃣ FAQ hay gặp nhất

Q1: Có nên tăng batch size lên mức tối đa của provider?
A: Không nhất thiết. Khi batch quá lớn latency tăng và khả năng thất bại một phần cũng tăng lên; thường chọn kích thước vừa phải (~80–120% của “optimal size” thu được qua benchmark).

Q2: Làm sao biết được một email trong batch bị reject vì spam?
A: Provider trả về trường errorCode cho từng mục; lọc ra các code như 550, 554 để đưa vào blacklist nội bộ.

Q3: Nếu queue bị đầy vì quá nhiều email chưa được xử lý thì sao?
A: Áp dụng back‑pressure: giảm tốc độ ingest mới hoặc mở rộng consumer pods ngay lập tức.

Q4: Có cần xác thực mỗi request batch không?
A: Có, dùng JWT hoặc HMAC ký payload; tránh reuse token cũ để bảo mật.


🔟 Giờ tới lượt bạn

Bạn đã thấy rõ lợi ích của việc nhóm yêu cầu API lại thành các batch lớn hơn rồi chưa? Hãy thử áp dụng những bước trên vào dự án hiện tại:

1️⃣ Kiểm tra giới hạn maxBatchSize của provider mà bạn đang dùng.
2️⃣ Thêm module Batch Builder vào pipeline hiện có (có thể copy‑paste mẫu code ở trên).
3️⃣ Chạy benchmark với ba kích thước batch khác nhau và ghi lại latency + tỷ lệ lỗi.
4️⃣ Điều chỉnh cho đến khi đạt được giảm ít nhất 70 % số request so với cách truyền thống.

Nếu gặp khó khăn hay muốn chia sẻ kết quả thử nghiệm, mình luôn sẵn sàng hỗ trợ!

Nếu anh em đang cần giải pháp trên, thử ngó qua con Serimi App xem, mình thấy API bên đó khá ổn cho việc scale. Hoặc liên hệ mình để được trao đổi nhanh hơn nhé.

Trợ lý AI của Hải
Nội dung được Hải định hướng, trợ lý AI giúp mình viết chi tiết.
Chia sẻ tới bạn bè và gia đình