Push Notification Story
01 / 13
একটা প্রশ্ন
৫,০০,০০০
ইউজারকে একসাথে নোটিফিকেশন পাঠাবেন কীভাবে?
একজন Admin → ৫ লাখ Device
Naive for-loop শুরু হলো
সব user RAM-এ → একে একে FCM call → request এখনো খোলা
🖥️API ServerRAM ↑ CPU ↑ Timeout...
27h
প্রায় ২৭ ঘণ্টা
Nginx 30s-এর আগেই request কেটে দিতে পারে
কাজটা API করবে না
API শুধু job তৈরি করবে, queue-তে রাখবে, log লিখবে, তারপর সঙ্গে সঙ্গে 200 OK.
📦Redis / BullMQJob persisted
Worker কাজ ধরলো
Queue থেকে job → database থেকে user page-by-page → পুরো ৫ লাখ কখনো RAM-এ আসে না।
🗄️PostgreSQLCursor pagination
Page size: 5,000 IDs
RAM stays stable
OFFSET ধীরে ধীরে ভেঙে পড়ে
skip: 495000 মানে Postgres আগে সেই row-গুলো পড়ে, তারপর ফেলে। Cursor সরাসরি last ID থেকে শুরু করে।
OFFSET: প্রতিটি page আরও ধীর
CURSOR: প্রায় constant speed
৫০০ করে batch
FCM এক call-এ সর্বোচ্চ ৫০০ token। তাই ৫ লাখ device = প্রায় ১,০০০ batch call.
✂️500
tokens / batchsendEachForMulticast()
Throttle না দিলে নিজের DB-ই বসে যাবে
সর্বোচ্চ ১০ batch/sec → ১০ × ৫০০ = ৫,০০০ notification/sec.
Throttle gate
0
notifications / sec
৫,০০,০০০ ÷ ৫,০০০ ≈ ১০০ সেকেন্ড
🗄️
☁️
এখন আসল delivery
Worker → FCM → হাজার হাজার ফোন। Offline device থাকলে FCM ধরে রাখে, online হলে পৌঁছে দেয়।
Duplicate? Redis lock আটকাবে
Admin double-click, retry, বা দুই worker একই job তুললেও SET key value NX EX — শুধু প্রথমজন ঢুকবে।
Network error? Retry হবে
BullMQ: attempts 3 + exponential backoff → 10s → 20s → 40s. Poison job অসীম সময় worker দখল করে রাখে না।
📡
FCM temporary failure
10s
Retry #1
Dead token নিজে থেকেই পরিষ্কার
FCM invalid / unregistered token ফেরত দিলে isValid=false. পরের campaign-এ সেগুলো query-তেই আসে না।
🧹
Token hygiene
প্রতিটি campaign table-কে cleaner করে
Notification feed-এ নতুন সমস্যা: Cache Stampede
Cache expire-এর মুহূর্তে ১০,০০০ user একসাথে DB-তে গেলে database শেষ। তাই singleFlight + Redis distributed lock.
🔐1 request → DBबाकিরা cache-এর জন্য wait
Final result
100s
৫ লাখ device broadcast
API ≈ 40ms
FCM bill = ৳0
Queue = Safety
Batch + Throttle = Speed
Redis Lock = Trust
Version Cache = Scale