PostgreSQL LISTEN/NOTIFY: লাইভ আপডেটের জন্য কখন এটা যথেষ্ট?
PostgreSQL LISTEN/NOTIFY কম কনফিগারেশনে লাইভ ড্যাশবোর্ড ও নোটিফিকেশন চালাতে পারে। কোথায় এটা মানায়, কি সীমা আছে, এবং কখন একটি মেসেজ ব্রোকার যোগ করা উচিত জানুন।

LISTEN/NOTIFY কোন সমস্যা সমাধান করতে চাইছে
পণ্যের UI-তে “লাইভ আপডেট” মানে সাধারণত হলো কোনো কিছু ঘটার পরেই স্ক্রিনে পরিবর্তন দেখা যায়, ব্যবহারকারী রিফ্রেশ না করলেও। একটি ড্যাশবোর্ডে সংখ্যা বাড়ে, ইনবক্সে লাল ব্যাজ আসে, অ্যাডমিন নতুন অর্ডার দেখে, অথবা একটি টোস্ট আসে “Build finished” বা “Payment failed” বলে। মূল বিষয় হলো টাইমিং: এটা তাত্ক্ষণিক মনে হয়, যদিও বাস্তবে এক বা দুই সেকেন্ড লাগতে পারে।
অনেক দল প্রথমে পোলিং দিয়ে শুরু করে: ব্রাউজার সার্ভারকে কয় সেকেন্ড অন্তর বলে “কিছু নতুন আছে?” পোলিং কাজ করে, তবে এর দুইটি সাধারণ সমস্যা আছে।
প্রথমত, এটা দেরি লাগাতে পারে কারণ ব্যবহারকারী পরিবর্তনটি পরবর্তী পোলেই দেখে।
দ্বিতীয়ত, এটা ব্যয়বহুল হতে পারে কারণ বারবার চেক করা হয় এমনকি যখন কিছুই পরিবর্তিত হয়নি। হাজার হাজার ব্যবহারকারীকে গুণ করলে এটা তাকিয়ে সার্ভারের উপর অতিরিক্ত লোড তৈরি করে।
PostgreSQL LISTEN/NOTIFY সহজ একটি কেসের জন্য আছে: “আমাকে বলো যখন কিছু বদলেছে।” বারবার জিজ্ঞাসা করার বদলে, আপনার অ্যাপ অপেক্ষা করে থাকতে পারে এবং ডাটাবেস ছোট একটি সিগন্যাল পাঠালে প্রতিক্রিয়া জানায়।
এটি এমন UI-গুলোর জন্য ভালো যেখানে একটি সূচকই যথেষ্ট। উদাহরণ:
- কোনো ড্যাশবোর্ড টাইল রিফ্রেশ করা উচিত কারণ টোটাল বদলেছে
- একটি ব্যাজ কাউন্ট আপডেট হওয়া উচিত কারণ নতুন আইটেম এসেছে
- একটি অভ্যন্তরীণ এলার্ট দেখা উচিত কারণ একটা জব স্ট্যাটাস বদলেছে
এইসব ক্ষেত্রে ট্রেডঅফ হল সরলতা বনাম নিশ্চিততা। LISTEN/NOTIFY যোগ করা সহজ কারণ এটা ইতোমধ্যে Postgres-এ আছে, কিন্তু এটি পূর্ণ মেসেজিং সিস্টেম নয়। নোটিফিকেশন হলো একটি হিন্ট, স্থায়ী রেকর্ড নয়। যদি একজন লিসেনার বিচ্ছিন্ন থাকে, সে সিগন্যাল মিস করতে পারে।
ব্যবহারিকভাবে এক উপায় হলো: NOTIFY আপনার অ্যাপকে জাগিয়ে তোলে, তারপর অ্যাপ টেবিল থেকে সত্যি অবস্থা পড়ে।
PostgreSQL LISTEN/NOTIFY কীভাবে কাজ করে (সরল ভাষায়)
PostgreSQL LISTEN/NOTIFY-কে ভাবুন এমন একটি সাধারণ ডোরবেল হিসেবে যা আপনার ডাটাবেসে builtin আছে। আপনার অ্যাপ বেল বাজার জন্য অপেক্ষা করতে পারে, আর সিস্টেমের অন্য অংশ কোনো পরিবর্তন হলে সেটা বাজাতে পারে।
একটি নোটিফিকেশনে দুইটি অংশ থাকে: একটি চ্যানেলের নাম এবং ঐচ্ছিক পে"];
A notification has two parts: a channel name and an optional payload. The channel is like a topic label (for example, orders_changed). The payload is a short text message you attach (for example, an order id). PostgreSQL doesn’t enforce any structure, so teams often send small JSON strings.
Who rings the bell?
A notification can be triggered from application code (your API server runs NOTIFY) or from the database itself using a trigger (a trigger runs NOTIFY after an insert/update/delete).
- App code is easier to follow and test.
- Triggers help when multiple writers touch the same tables and you want consistent behavior.
On the receiving side, your app server opens a database connection and runs LISTEN channel_name. That connection stays open. When a NOTIFY channel_name, 'payload' happens, PostgreSQL pushes a message to all connections listening on that channel. Your app then reacts (refresh cache, fetch the changed row, push a WebSocket event to the browser, and so on).
What NOTIFY does (and does not do)
NOTIFY is best understood as a signal, not a delivery service:
- It tells listeners that “something happened” right now.
- It doesn’t guarantee every message is received (a disconnected listener misses it).
- It’s not a queue (messages aren’t stored for later).
- The payload is size-limited and should stay small.
Used this way, PostgreSQL LISTEN/NOTIFY can power live UI updates without adding extra infrastructure.
কখন LISTEN/NOTIFY লাইভ UI আপডেটের জন্য যথেষ্ট
LISTEN/NOTIFY উজ্জ্বল হয় যখন আপনার UI-কে শুধু একটি নাজ (nudge) দরকার যে কিছু বদলেছে, পূর্ণ ইভেন্ট স্ট্রিম নয়। ভাবুন “এই উইজেটটি রিফ্রেশ কর” বা “একটি নতুন আইটেম এসেছে” — পুরো প্রতিটি ক্লিক প্রক্রিয়াকরণ দরকার নেই।
এটি সবচেয়ে ভালো কাজ করে যখন ডাটাবেসই আপনার সোলস-অফ-ট্রুথ এবং আপনি চান UI সেটির সাথে সিঙ্ক থাকবে। একটি সাধারণ প্যাটার্ন হলো: রো লিখুন, একটি ছোট নোটিফিকেশন পাঠান যাতে একটি ID থাকে, এবং UI (বা API) লেটেস্ট স্টেট রিকোয়েস্ট করুক।
LISTEN/NOTIFY সাধারণত পর্যাপ্ত যখন এসব সত্যি:
- মেসেজটি একটি ছোট “কিছু বদলেছে” সিগন্যাল, পূর্ণ পে"];
The message is a small “something changed” signal, not a full payload.
- Event volume is low to moderate (bursty is fine, sustained high throughput is not).
- If a user misses a notification, the UI can recover by reloading or briefly polling.
- You value simplicity more than perfect delivery guarantees (common for early products and internal tools).
- You have a single primary database and want fewer moving parts.
A concrete example: an internal support dashboard shows “open tickets” and a badge for “new notes.” When an agent adds a note, your backend writes it to Postgres and NOTIFYs ticket_changed with the ticket ID. The browser receives it over a WebSocket connection and refetches that one ticket card. No extra infrastructure, and the UI still feels live.
কোথায় LISTEN/NOTIFY ভেঙে পড়ে
LISTEN/NOTIFY প্রথম দিকে খুব ভালো মনে হতে পারে, কিন্তু এর কঠোর সীমা আছে। এই সীমাগুলো তখন দেখা দেয় যখন আপনি নোটিফিকেশনকে একটি মেসেজ সিস্টেম হিসেবে ব্যবহার করতে থাকেন বদলে এটাকে একটি হালকা “ঠেকানো” হিসেবে ধরে।
সবচেয়ে বড় ফারাক হল স্থায়িত্ব (durability)। একটি NOTIFY কোনো কিউড জব নয়। যদি কেউ লিসেনিং না করে তখন পাঠানো হয়, সেই মেসেজ মিস হয়ে যায়। এমনকি যখন লিসেনার কানেক্টেড থাকে, ক্র্যাশ, ডেপ্লয়, নেটওয়ার্ক বিঘ্ন, বা ডাটাবেস রিস্টার্ট সংযোগ ভেঙে দিতে পারে। আপনি স্বয়ংক্রিয়ভাবে “মিসড” নোটিফিকেশন ফিরে পাবেন না।
ডিসকানেক্টগুলো ইউজার-ফেসিং ফিচারের জন্য বিশেষভাবে কষ্টদায়ক। ধরুন একটি ড্যাশবোর্ড নতুন অর্ডার দেখায়। একটি ব্রাউজার ট্যাব স্লিপ করে, WebSocket পুনরায় কানেক্ট করে, এবং UI “অবস্থা স্থির” মনে হতে পারে কারণ কিছু ইভেন্ট মিস হয়েছে। আপনি এই সমস্যা ঘোচাতে পারবেন, কিন্তু সেই ওয়ার্কারাউন্ড আর হতে হবে না “শুধু LISTEN/NOTIFY”: আপনাকে স্টেট পুনর্নির্মাণ করতে হবে ডাটাবেস থেকে এবং NOTIFY কে কেবল রিফ্রেশের হিন্ট হিসেবে রাখবেন।
ফ্যান-আউটও আরেকটি সমস্যা। একটি ইভেন্ট শত শত বা হাজার হাজার লিসেনারকে জাগাতে পারে (অনেক অ্যাপ সার্ভার, অনেক ইউজার)। যদি আপনি একটি বিশাল চ্যানেল যেমন orders ব্যবহার করেন, প্রতিটি লিসেনার জেগে পড়ে যদিও শুধুমাত্র একজন ইউজার সেই ইভেন্টের প্রতি আগ্রহী। এ থেকে CPU এবং কানেকশন চাপ বাড়ে।
পেলোড সাইজ এবং ফ্রিকোয়েন্সিও জটিলতা বাড়ায়। NOTIFY পেলোড ছোট, এবং উচ্চ-ফ্রিকোয়েন্সি ইভেন্টগুলো ক্লায়েন্ট-সাইড হ্যান্ডলারের থেকে দ্রুত ভারী হয়ে উঠতে পারে।
নিচের লক্ষণগুলো দেখুন:
- আপনি নিশ্চিত ডেলিভারি, রিট্রাই বা অর্ডারিং চান।
- ক্লায়েন্টেরা ঘন ঘন রিকানেক্ট করে এবং কোনো আপডেট মিস করলে চলবে না।
- একটি চ্যানেল অনেক লিসেনারকে জাগায় যারা প্রধানত ইভেন্টটি উপেক্ষা করে।
- আপনি বড় পেলোড পাঠাচ্ছেন বা প্রতি সেকেন্ড বেশ কয়েকটি ইভেন্ট পাঠাচ্ছেন।
এ ধরনের হলে, NOTIFY কে একটি “থাপ্পড়” হিসেবে রাখুন এবং নির্ভরযোগ্যতা টেবিল বা সঠিক মেসেজ ব্রোকারে স্থানান্তর করুন।
ধাপে ধাপে: একটি সরল প্যাটার্ন যা কাজ করে
একটি নির্ভরযোগ্য প্যাটার্ন হলো NOTIFY-কে একটি নাজ হিসেবে বিবেচনা করা, উৎস সত্যি ডাটাবেস রো টেবিল পর্যন্ত রাখা। ডাটাবেস রো হলো সত্য; নোটিফিকেশন বলে কখন দেখা উচিত।
1) লিখুন, কমিট করুন, তারপর নোটিফাই করুন
লিখাটি একটি ট্রানজেকশনের ভিতরে করুন, এবং কেবল ডাটা চেঞ্জ কমিট হওয়ার পরেই নোটিফিকেশন পাঠান। যদি আপনি খুব অর্লি নোটিফাই করেন, ক্লায়েন্ট জেগে উঠে কিন্তু ডাটা এখনও না পেয়ে বিভ্রান্ত হবে।
একটি সাধারণ সেটআপ হলো এমন ট্রিগার যা INSERT/UPDATE-এ ফায়ার করে এবং একটি ছোট মেসেজ পাঠায়।
NOTIFY dashboard_updates, '{"type":"order_changed","order_id":123}'::text;
2) সাধারণ চ্যানেল নাম এবং ছোট পেলোড বেছে নিন
চ্যানেল নামকরণ তখন ভালো কাজ করে যখন এটা সিস্টেম সম্পর্কে মানুষের ভাবনার মতো মিলে। উদাহরণ: dashboard_updates, user_notifications, বা প্রতি-টেন্যান্ট হিসেবে tenant_42_updates।
পেলোড ছোট রাখুন। আইডি এবং একটি টাইপ রাখুন, পুরো রেকর্ড নয়। একটি দরকারী ডিফল্ট আকার হলো:
type(কি ঘটেছে)id(কি বদলেছে)- ঐচ্ছিক
tenant_idবাuser_id
এতে ব্যান্ডউইডথ কম থাকে এবং নোটিফিকেশন লগে সংবেদনশীল ডাটা লিক হওয়ার ঝুঁকি কমে।
3) রিকানেক্ট হ্যান্ডল করুন এবং প্রতিটি কানেকশনে পুনরায় সাবস্ক্রাইব করুন
কানেকশন পড়ে যায়। এর জন্য পরিকল্পনা রাখুন।
কানেক্ট হলে, প্রয়োজনীয় সব চ্যানেলের জন্য LISTEN চালান। ডিসকানেক্ট হলে, ছোট ব্যাকঅফ নিয়ে পুনরায় কানেক্ট করুন। রিকানেক্ট হলে আবার LISTEN চালান (সাবস্ক্রিপশনগুলো ধরে রাখা হয় না)। রিকানেক্টের পর শক্তসারি হিসেবে সাম্প্রতিক পরিবর্তনগুলো দ্রুত রিফেচ করুন যাতে মিস হওয়া ইভেন্টগুলো কভার হয়।
4) UI আপডেট: প্রথমে রিফেচ করুন, পরে প্যাচ করুন
অধিকাংশ লাইভ UI আপডেটে রিফেচ করাই সবচেয়ে নিরাপদ: ক্লায়েন্ট {type, id} পায় তারপর সার্ভারকে নতুন স্টেট চাইতে বলে।
ইঙ্ক্রিমেন্টাল প্যাচ দ্রুত হতে পারে, কিন্তু ভুল হওয়ার সম্ভাবনাও বেশি (আউট-অফ-অর্ডার ইভেন্ট, আংশিক ব্যর্থতা)। একটি ভালো মধ্যপন্থা হলো: ছোট স্লাইসগুলো রিফেচ করুন (একটি অর্ডার রো, একটি টিকিট কার্ড, একটি ব্যাজ কাউন্ট) এবং ভারী অগ্রেগেটগুলোকে ছোট টাইমারে রাখুন।
ড্যাশবোর্ড এবং নোটিফিকেশনের জন্য স্কেলিং প্যাটার্ন
যখন আপনি একাধিক অ্যাডমিন ড্যাশবোর্ড থেকে অনেক ব্যবহারকারীকে একই সংখ্যা দেখাতে শুরু করবেন, ভাল অভ্যাস SQL-চক্রের চেয়েও বেশি গুরুত্বপূর্ণ। LISTEN/NOTIFY এখনও ভাল কাজ করতে পারে, কিন্তু আপনাকে ইভেন্টগুলোর প্রবাহ কিভাবে ব্রাউজার পর্যন্ত আসে সেটি গঠন করতে হবে।
একটি সাধারণ বেসলাইন হলো: প্রতিটি অ্যাপ ইনস্ট্যান্স একটি দীর্ঘজীবী কানেকশন খোলে যা LISTEN করে, তারপর সংযুক্ত ক্লায়েন্টদের কাছে আপডেট পুশ করে। "এক ইনস্ট্যান্সে এক লিসেনার" সেটআপ সহজ এবং ছোট সংখ্যার অ্যাপ সার্ভার থাকলে বেশ ভাল কাজ করে যদি আপনি সাময়িক রিকানেক্ট সহ্য করতে পারেন।
যদি আপনার অনেক অ্যাপ ইনস্ট্যান্স (বা সার্ভারলেস ওয়ার্কার) থাকে, একটি শেয়ার্ড লিসেনার সার্ভিস সহজ হতে পারে। একটি ছোট প্রসেস একবার লিসেন করে, তারপর বাকি স্ট্যাককে ফ্যান-আউট করে। এছাড়া এখানে ব্যাচিং, মেট্রিকস, এবং ব্যাকপ্রেশার যোগ করার একটি একক স্থান থাকে।
ব্রাউজারের জন্য, সাধারণত WebSockets (দুই-মুখী, ইন্টারেক্টিভ UI-র জন্য দুর্দান্ত) বা Server-Sent Events (SSE) (এক-মুখী, ড্যাশবোর্ডের জন্য সরল) ব্যবহার করা হয়। যাই হোক, "সবকিছু রিফ্রেশ কর" পাঠানো থেকে বিরত থাকুন। "order 123 changed" মতো কমপ্যাক্ট সিগন্যাল পাঠান যাতে UI শুধুমাত্র যা দরকার তা রিফেচ করে।
UI কাঁপা (thrashing) এড়াতে কিছু শ্রদ্ধা যোগ করুন:
- বুস্ট কমাতে ডিবাউন্স করুন (উদাহরণ: 100 থেকে 500 ms)
- ডুপ্লিকেট গুলো একীভূত করুন (একই রেকর্ড 10 বার বদলে গেলে 1 আপডেট পাঠান)
- পুরো পেজ রিফ্রেশ না করে উইজেটে “ডার্টি ফ্ল্যাগ” রাখুন
চ্যানেল ডিজাইনও গুরুত্বপূর্ণ। একটিভ গ্লোবাল চ্যানেল ব্যবহারের বদলে, টেন্যান্ট, টিম বা ফিচার অনুযায়ী পার্টিশন করুন যাতে ক্লায়েন্টরা কেবল প্রাসঙ্গিক ইভেন্টই পায়। উদাহরণ: notify:tenant_42:billing এবং notify:tenant_42:ops।
সাধারণ ভুল এবং কীভাবে সেগুলো এড়াবেন
LISTEN/NOTIFY সহজ মনে হওয়ায় দলগুলো তা দ্রুত শিপ করে এবং পরে প্রোডাকশনে অবাক হয়। বেশিরভাগ সমস্যা আসে যখন এটাকে একটি গ্যারান্টিড মেসেজ কিউ হিসেবে ধরা হয়।
1) নোটিফিকেশনকে স্থায়ী বার্তা মনে করা
আপনার অ্যাপ রিকানেক্ট করলে (ডেপ্লয়, নেটওয়ার্ক ব্লিপ, DB failover) NOTIFY-এ পাঠানো কোনো বার্তাই যদি সেই সময়ে কানেক্টেড না থাকলে চলে যাবে। সমাধান: নোটিফিকেশনকে সিগন্যাল মনে করুন, তারপর ডাটাবেস পুনরায় চেক করুন।
একটি ব্যবহারিক প্যাটার্ন: বাস্তব ইভেন্ট একটি টেবিলে স্টোর করুন (id এবং created_at সহ), তারপর রিকানেক্টে আপনার শেষ দেখা id থেকে নতুনগুলো ফেচ করুন।
2) পেলোড ওভারলোড করা
LISTEN/NOTIFY পেলোড বড় JSON ব্লবের জন্য নয়। বড় পেলোড অতিরিক্ত কাজ দেয়, বেশি পার্সিং লাগে, এবং সীমা টাপার সম্ভাবনা বাড়ায়।
পেলোড ছোট হোক: "order:123" ধরনের হিন্ট পাঠান। তারপর অ্যাপ ডাটাবেস থেকে আঠালো স্টেট পড়ুক।
3) “সিগন্যাল” এবং “ডাটা ফেচ” মিশিয়ে ফেলা
একজন সাধারণ ভুল হলো ক্লায়েন্টকে পেলোডের উপরে নির্ভর করা যাতে মনে হয় পেলোডই সুত্র। এতে স্কীমা পরিবর্তন এবং ক্লায়েন্ট ভার্সন মেনেজ করা কষ্টকর হয়।
পরিষ্কার বিভাজন রাখুন: কিছু বদলেছে বলে নোটিফাই করুন, তারপর সাধারন কুয়েরি দিয়ে বর্তমান ডাটা নিন।
4) ট্রিগার যারা খুব ঘন ঘন ফায়ার করে
প্রতিটি রো চেঞ্জে ট্রিগার করে NOTIFY করলে সিস্টেম বিস্ফোরিত হতে পারে, বিশেষত ব্যস্ত টেবিলের ক্ষেত্রে।
শুধুমাত্র তাৎপর্যপূর্ণ ট্রানজিশনগুলোতে নোটিফাই করুন (উদাহরণ: স্ট্যাটাস বদল)। যদি আপডেট খুবই noisy হয়, ব্যাচ করুন (প্রতি ট্রানজেকশন বা টাইম উইন্ডোতে একবার নোটিফাই) অথবা ঐ আপডেটগুলোকে নোটিফাই-পথ থেকে সরিয়ে দিন।
5) UI-তে ব্যাকপ্রেশার উপেক্ষা করা
ডাটাবেস নোটিফিকেশন পাঠালে UI তবুও থমকে যেতে পারে। প্রতিটি ইভেন্টে রেন্ডার করে এমন ড্যাশবোর্ড জমে যেতে পারে।
ক্লায়েন্টে ডিবাউন্স করুন, বোর্স্টগুলো একত্র করুন, এবং প্রতিটি ডেল्टा প্রয়োগ করার বদলে “ইনভ্যালিডেট এবং রিফেচ” পছন্দ করুন। উদাহরণ: একটি নোটিফিকেশন বেড়ান দ্রুত আপডেট দেখাতে পারে, কিন্তু ড্রপডাউন লিস্টটি কয়েক সেকেন্ডে একবার রিফ্রেশ করা যেতে পারে।
দ্রুত চেকলিস্ট: LISTEN/NOTIFY মানায় কিনা নির্ধারণ করুন
LISTEN/NOTIFY দুর্দান্ত যখন আপনি একটি ছোট “কিছু বদলেছে” সিগন্যাল চাচ্ছেন যাতে অ্যাপ নতুন ডাটা ফেচ করে। এটা একটি সম্পূর্ণ মেসেজিং সিস্টেম নয়।
বিল্ড করার আগে এই প্রশ্নগুলোর উত্তর দিন:
- যদি একটি লিসেনার এক মিনিটের জন্য অফলাইনে থাকে, ঘটনাগুলো মিস করা চলবে? যদি মিস হওয়া unacceptable হয়, তাহলে আপনাকে ডিউরেবল ডেলিভারি ও রেপ্লে দরকার।
- কি “নিয়তি নিকট-রিয়েল-টাইম” যথেষ্ট? যদি ব্যবহারকারীরা ডেপ্লয় বা নেটওয়ার্ক সমস্যা সময় সাময়িক বিলম্ব সহ্য করতে পারে,
LISTEN/NOTIFYসাধারণত ঠিক আছে। - আপনার পিক বুস্ট রেট কি? কয়েকটি ইভেন্ট/সেকেন্ড বা মাঝেমধ্যে স্পাইক হলে এটি ভালো; দীর্ঘস্থায়ী উচ্চ ভলিউম চ্যানেলগুলোকে নোয়িসি করে তোলে।
- কতজন কনজিউমার একসাথে লিসেন করবে? একটি বা কয়েকটি ব্যাকএন্ড ওয়ার্কার সহজ। শত বা হাজার কানেক্টেড ক্লায়েন্ট হলে আপনি ফ্যান-আউট লেয়ার (আপনার সার্ভার) চান যাতে প্রত্যেকে সরাসরি লিসেন না করে।
- আপনি কি কঠোর অর্ডারিং চান বিভিন্ন ইভেন্ট টাইপ জুড়ে? যদি আপনার ড্যাশবোর্ডে “A আগে প্রসেস হওয়া দরকার B”-র মত শর্ত থাকে, তা দ্রুত জটিল হয়ে যায়।
একটি ব্যবহারিক নিয়ম: NOTIFY-কে যদি আপনি একটি নাজ হিসেবে ব্যবহার করতে পারেন ("পুনঃপাঠ কর ওই রো"), তাহলে আপনি নিরাপদ অঞ্চলে আছেন।
উদাহরণ: একটি অ্যাডমিন ড্যাশবোর্ড নতুন অর্ডার দেখায়। যদি একটি নোটিফিকেশন মিস হয়, পরবর্তী পোল বা পেজ রিফ্রেশ সঠিক কন্ট্ট দেখায়। এটা একটি ভালো ফিট। কিন্তু যদি আপনি "এই কার্ডকে চার্জ কর" বা "এই প্যাকেজ শিপ কর"—যেটা মিস হলে বড় সমস্যা—ইভেন্ট পাঠাচ্ছেন, তখন মিস হওয়া বিপজ্জনক।
উদাহরণ: লাইভ ড্যাশবোর্ড এবং ইউজার নোটিফিকেশন
একটি ছোট সেলস অ্যাপ চিন্তা করুন: একটি ড্যাশবোর্ডে আজকের রেভিনিউ, মোট অর্ডার এবং একটি “রিসেন্ট অর্ডার” তালিকা দেখায়। একই সময়ে প্রতিটি সেলসপারসনকে একটি ছোট নোটিফিকেশন পেতে হবে যখন তাদের অর্ডার পেইড বা শিপড হয়।
সরল পদ্ধতি হলো PostgreSQL-কে সূত্র-অফ-ট্রুথ ধরে রাখা, এবং LISTEN/NOTIFY কে কেবল থাপ্পড় হিসেবে ব্যবহার করা যে কিছু বদলেছে।
যখন একটি অর্ডার তৈরি হয় বা স্ট্যাটাস বদলে, আপনার ব্যাকএন্ড একটি অনুরোধে দুইটি কাজ করবে: রো লিখবে (বা আপডেট করবে) এবং তারপর ছোট পেলোডের সঙ্গে NOTIFY পাঠাবে (সাধারণত শুধুমাত্র অর্ডার ID এবং ইভেন্ট টাইপ)। UI সম্পূর্ণরূপে NOTIFY পেলোডের উপর নির্ভর করবে না।
একটি ব্যবহারিক ফ্লো দেখতে এমন:
- ট্রানজেকশনে অর্ডার চেঞ্জ লিখুন।
- কমিটের পরে,
{"type":"status_changed","order_id":123}সহNOTIFY orders_eventsপাঠান। - একটি ব্যাকএন্ড লিসেনার ইভেন্ট গ্রহণ করে এবং সেগুলো কানেক্টেড ব্রাউজারদের কাছে পুশ করে (WebSocket বা SSE)।
- ড্যাশবোর্ড প্রয়োজনীয় জিনিসগুলো রিফেচ করে: ID দিয়ে নির্দিষ্ট অর্ডার রো এবং টোটালগুলো ছোট টাইমারে (উদাহরণ 2-5 সেকেন্ড) রিফ্রেশ করে।
- ইউজার নোটিফিকেশন টার্গেটেড: শুধুমাত্র সেই সেলসপারসন যিনি ঐ অর্ডারের মালিক, তিনি "paid" বা "shipped" টোস্ট পাবেন।
এতে NOTIFY হালকা থাকে এবং ব্যয়বহুল কুয়েরিগুলো সীমাবদ্ধ হয়।
ট্রাফিক বাড়লে ফাটল দেখা দেয়: ইভেন্ট স্পাইক একটি লিসেনারকে ওভারহেল্ম করতে পারে, রিকানেক্টে নোটিফিকেশন মিস হতে পারে, এবং আপনাকে গ্যারান্টিড ডেলিভারি ও রেপ্লের প্রয়োজন পড়তে পারে। তখন সাধারণত আপনি একটি আরো নির্ভরযোগ্য স্তর যোগ করেন (আউটবক্স টেবিল + ওয়ার্কার, পরে প্রয়োজনে ব্রোকার) এবং Postgres-কে সূত্র-অফ-ট্রুথ রাখেন।
কখন ডেডিকেটেড ব্রোকার নেওয়া উচিত
LISTEN/NOTIFY দ্রুত একটি “কিছু বদলেছে” সিগন্যাল দিতে ভালো। এটি পূর্ণ মেসেজিং সিস্টেম হিসেবে তৈরি নয়। যখন আপনি ইভেন্টদের উপর ভিত্তি করে কাজগুলোকে উৎস হিসেবে নির্ভর করতে শুরু করেন, তখন ব্রোকার নেওয়ার সময় এসেছে।
স্পষ্ট সংকেত যে আপনি LISTEN/NOTIFY ছেড়ে দিয়েছেন
নিচের যেকোনোটির উপস্থিতি হলে ব্রোকার আপনাকে ঝামেলা থেকে রক্ষা করবে:
- আপনাকে ডিউরেবিলিটি দরকার: অ্যাপ রিস্টার্ট বা ডেটাবেস ফেইলওভারে ইভেন্টগুলো হারানো যাবে না।
- আপনাকে রিট্রাই এবং ডেড-লেটার হ্যান্ডলিং দরকার: যদি কনজিউমার ব্যর্থ হয়, মেসেজ পুনরায় চেষ্টা করা এবং ট্র্যাক করা দরকার।
- কনজিউমার গ্রুপ দরকার: একাধিক ওয়ার্কার লো শেয়ার করে ডবল প্রসেসিং ছাড়া।
- অডিটিং এবং রেপ্লে দরকার: “গত এক ঘণ্টায় যা ঘটেছে দেখাও” বা “ইভেন্ট থেকে ভিউ পুনর্নির্মাণ কর”।
- কন্ট্রোলড ব্যাকপ্রেশার দরকার: প্রডিউসারগুলো ধীর কনজিউমারকে ওভারহেল্ম না করুক।
LISTEN/NOTIFY মেসেজগুলো লATER-এর জন্য সংরক্ষণ করে না। এটা একটি পুশ সিগন্যাল, একটি পারসিস্টেড লগ নয়। ড্যাশবোর্ড উইজেট রিফ্রেশ করার জন্য এটি পারফেক্ট, কিন্তু “বিলিং চালাও” বা “শিপ কর” ধাঁচের জবগুলোর জন্য ঝুঁকিপূর্ণ।
ব্রোকার কী যোগ করে (সরল ভাষায়)
একটি ব্রোকার আপনাকে বাস্তব মেসেজ ফ্লো মডেল দেয়: কিউ (কাজ), টপিক (অনেককে সম্প্রচার), রিটেনশন (মেসেজ মিনিট থেকে দিন পর্যন্ত রাখা), এবং অ্যাকনলেজমেন্ট (কনজিউমার প্রক্রিয়াকরণ নিশ্চিত করে)। এতে আপনি “ডাটাবেস বদলে গেছে” এবং “যা ঘটার কথা ছিল তা সব ঘটুক” আলাদা করতে পারেন।
সবচেয়ে জটিল টুল না নিলেও চলে। সাধারণ অপশনগুলো হলো Redis (pub/sub বা streams), NATS, RabbitMQ, এবং Kafka। সঠিক পছন্দ নির্ভর করে আপনি কি সরল কাজ-কিউ চান, অনেক সার্ভিসে ফ্যান-আউট চান, বা ইতিহাস রেপ্লে করার দরকার আছে কি না।
ধীরে ধীরে মাইগ্রেশন প্ল্যান
বড় রিরাইট ছাড়াই সরানো যায়। একটি ব্যবহারিক প্যাটার্ন হলো NOTIFY কেবল UI-কে জাগানোর জন্য রাখুন আর ব্রোকার ধীরে ধীরে ডেলিভারির সূত্র হয়ে উঠুক।
শুরুতে আপনার ব্যবসায়িক চেঞ্জের সঙ্গে একই ট্রানজেকশনে একটি “ইভেন্ট রো” লিখুন, তারপর একটি ওয়ার্কার ঐ ইভেন্ট ব্রোকারে প্রকাশ করুক। ট্রানজিশনে, NOTIFY এখনও UI লেয়ারকে বলে “নতুন ইভেন্ট আছে পরীক্ষা কর”, আর ব্যাকগ্রাউন্ড ওয়ার্কাররা ব্রোকার থেকে রিট্রাই এবং অডিটিং-এর সাথে কনজিউম করে।
এইভাবে ড্যাশবোর্ড দ্রুত থাকে, আর সমালোচনামূলক ওয়ার্কফ্লোগুলো আর বেষ্ট-এফোর্ট নোটিফিকেশনের উপর নির্ভর করে না।
পরবর্তী ধাপ: ছোটভাবে চালান এবং নিরাপদে ইটারেট করুন
একটি স্ক্রিন বেছে নিন (একটি ড্যাশবোর্ড টাইল, একটি ব্যাজ কাউন্ট, একটি “নতুন নোটিফিকেশন” টোস্ট) এবং তা এন্ড-টু-এন্ড ওয়ার করা পর্যন্ত বুনুন। LISTEN/NOTIFY দিয়ে আপনি দ্রুত একটি ব্যবহারযোগ্য রেজাল্ট পেতে পারেন, যতক্ষণ আপনি স্কোপ ছোট রাখেন এবং বাস্তব ট্রাফিকে মাপেন।
সর্বোপ্রথম সিম্পল রিলায়েবল প্যাটার্ন শুরু করুন: রো লিখুন, কমিট করুন, তারপর ছোট সিগন্যাল ইমিট করুন যে কিছু বদলেছে। UI-তে সিগন্যাল পেলে দরকারি অংশ রিফেচ করুন। এতে পেলোড ছোট থাকে এবং আউট-অফ-অর্ডার অ্যাররে subtle বাগ এড়ানো যায়।
শুরুতেই মৌলিক অবজার্ভিবিলিটি যোগ করুন। ফ্যান্সি টুল দরকার নেই, কিন্তু যখন সিস্টেম noisy হয় তখন উত্তর জানতে হবে:
- রিকানেক্ট এবং সাবস্ক্রিপশন শুরু লগ করুন (কারণসহ)
- নোটিফাই রেট এবং পিক বুস্ট ট্র্যাক করুন
- নোটিফিকেশন থেকে ট্রিগার হওয়া ফেচ রেট ট্র্যাক করুন
- “মিসড আপডেট” লক্ষণ দেখুন (ব্যবহারকারীরা রিফ্রেশ করতে হচ্ছে)
চুক্তি (contracts) সহজ রাখুন এবং লিখে রাখুন। চ্যানেল নাম, ইভেন্ট নাম এবং পেলোডের আকার (যদি থাকে) নির্ধারণ করুন। আপনার রিপোতে একটি ছোট “ইভেন্ট ক্যাটালগ” থাকলে ড্রিফ্ট রোধ হয়।
যদি আপনি দ্রুত বানাতে চান এবং স্ট্যাক কম রাখতে চান, Koder.ai (koder.ai) মত প্ল্যাটফর্ম প্রথম ভার্সন শিপ করতে সাহায্য করতে পারে: React UI, Go ব্যাকএন্ড, এবং PostgreSQL দিয়ে শুরু করে, পরে প্রয়োজন অনুযায়ী ইটারেট করুন।
সাধারণ প্রশ্ন
What is PostgreSQL LISTEN/NOTIFY actually good for?
Use LISTEN/NOTIFY when you only need a quick signal that something changed, like refreshing a badge count or a dashboard tile. Treat the notification as a nudge to refetch the real data from tables, not as the data itself.
Why would I use LISTEN/NOTIFY instead of polling?
Polling checks for changes on a schedule, so users often see updates late and your server does work even when nothing changed. LISTEN/NOTIFY pushes a small signal right when the change happens, which usually feels faster and avoids lots of empty requests.
Does LISTEN/NOTIFY guarantee delivery?
No, it’s best-effort. If the listener is disconnected during a NOTIFY, it can miss the signal because notifications aren’t stored for later replay.
What should I put in the NOTIFY payload?
Keep it small and treat it as a hint. A practical default is a tiny JSON string with a type and an id, then have your app query Postgres for the current state.
Should I notify before or after committing a transaction?
A common pattern is to send the notification only after the write is committed. If you notify too early, a client can wake up and not find the new row yet.
Should I send NOTIFY from application code or from a trigger?
Application code is usually easier to understand and test because it’s explicit. Triggers are useful when many different writers touch the same table and you want consistent behavior no matter who made the change.
How do I handle reconnects without missing updates?
Plan for reconnects as normal behavior. On reconnect, re-run LISTEN for the channels you need and do a quick refetch of recent state to cover anything you might have missed while offline.
How do I get database notifications into the browser?
Don’t have every browser connect to Postgres. A typical setup is one long-lived listener connection per backend instance, then your backend forwards events to browsers via WebSockets or SSE and the UI refetches what it needs.
How do I avoid waking up too many listeners or flooding the UI?
Use narrower channels so only the right consumers wake up, and batch noisy bursts. Debouncing for a few hundred milliseconds and coalescing duplicate updates keeps your UI and backend from thrashing.
When should I stop using LISTEN/NOTIFY and move to a broker?
Graduate when you need durability, retries, consumer groups, ordering guarantees, or auditing/replay. If missing an event would cause a real incident (billing, shipping, irreversible workflows), use an outbox table plus a worker or a dedicated broker instead of relying on NOTIFY alone.