ব্লগে ফিরে যান

navigator.sendBeacon বনাম fetch keepalive: নেভিগেশন আটকে না রেখে নির্ভরযোগ্য অ্যানালিটিক্স ডেলিভারি

নেভিগেশন থামিয়ে না দিয়ে পেজ আনলোড টিকিয়ে রাখতে পারে এমন অ্যানালিটিক্স পেলোড কীভাবে পাঠাবেন — এবং কুকিহীন কালেকশনের জন্য sendBeacon ও fetch keepalive-এর মধ্যে বেছে নেওয়াটা কেন গুরুত্বপূর্ণ।

পেজ যতক্ষণ সক্রিয় থাকে, ততক্ষণ একটি পেজ ভিউ বা ইভেন্ট রেকর্ড করা সহজ। কঠিন অংশটি হলো, ব্যবহারকারী যখন অন্য কোথাও চলে যান, ট্যাব বন্ধ করেন, বা মোবাইলে অ্যাপ ব্যাকগ্রাউন্ডে পাঠান, ঠিক সেই মুহূর্তে শেষ পেলোডটি পৌঁছে দেওয়া। এটি ভুল করলে হয় ডেটা হারাবেন, অথবা তার চেয়েও খারাপ, ব্যবহারকারী যে নেভিগেশনটি চেয়েছিলেন সেটিই বিলম্বিত হয়ে যাবে। এই লেখায় দুটি বাস্তবসম্মত বিকল্প তুলনা করা হয়েছে — navigator.sendBeacon() এবং keepalive: true সহ fetch() — এবং ব্যাখ্যা করা হয়েছে Monoid-এর ট্র্যাকার আসলে কোনটি ব্যবহার করে, এবং কেন, তাও আবার কোনো কুকি, স্টোরেজ বা ফিঙ্গারপ্রিন্টিং ছাড়াই।

কেন আনলোড একটি বিশেষ পরিস্থিতি

যখন একটি ডকুমেন্ট আনলোড হতে শুরু করে, ব্রাউজার তখন বন্ধ হয়ে যাওয়ার প্রক্রিয়ায় থাকে। ঐতিহাসিকভাবে সিঙ্ক্রোনাস XMLHttpRequest-কে এখানেই অপব্যবহার করা হয়েছে — একটি রিকোয়েস্ট সম্পূর্ণ না হওয়া পর্যন্ত নেভিগেশন আটকে রাখতে — যা রেসপন্সিভনেসের ক্ষতি করে। আধুনিক ব্রাউজারগুলো এটিকে সক্রিয়ভাবে নিরুৎসাহিত করে, এবং unload-এর সময় শুরু হওয়া রিকোয়েস্টগুলো প্রায়শই বাতিল হয়ে যায়। Beacon স্পেসিফিকেশনটি ঠিক এই সমস্যা সমাধানের জন্যই তৈরি: এটি একটি পেজকে এমন একটি রিকোয়েস্ট শিডিউল করতে দেয় যা ইউজার এজেন্ট asynchronously পাঠানোর চেষ্টা করার নিশ্চয়তা দেয়, পরবর্তী পেজ লোড হওয়া আটকে না রেখে।

অ্যানালিটিক্সের জন্য এর ব্যবহারিক তাৎপর্য হলো: beforeunload/unload হ্যান্ডলার থেকে চালু করা সাধারণ fetch বা XHR-এর উপর কখনোই ভরসা করবেন না। এটি হয়তো ডিভাইস থেকে বেরিয়েই যেতে পারবে না।

navigator.sendBeacon

sendBeacon ঠিক এই কাজের জন্যই তৈরি করা হয়েছে। এটি একটি ছোট POST কিউতে রাখে এবং সঙ্গে সঙ্গে একটি বুলিয়ান মান ফেরত দেয়, যা কেবল জানায় রিকোয়েস্টটি সফলভাবে কিউতে যুক্ত হয়েছে কিনা — এটি সফল হয়েছে কিনা তা নয়। এরপর ব্রাউজার সেটি ব্যাকগ্রাউন্ডে পাঠিয়ে দেয়, এমনকি পেজ চলে যাওয়ার পরও।

const ok = navigator.sendBeacon('/collect', payload);

এর শক্তিশালী দিকগুলো:

  • ট্রান্সফারটি ডকুমেন্টের জীবনকালের সাথে বাঁধা নয়।
  • এটি কম অগ্রাধিকারসম্পন্ন এবং পরবর্তী নেভিগেশনের সাথে প্রতিযোগিতা না করার জন্য ডিজাইন করা।
  • কোনো রেসপন্স হ্যান্ডেল করার প্রয়োজন নেই, তাই অপেক্ষা করার মতো কিছু নেই।

স্পেসিফিকেশন এবং ব্রাউজার ইমপ্লিমেন্টেশন অনুযায়ী সীমাবদ্ধতাগুলো:

  • শুধুমাত্র POST। আপনি ইচ্ছেমতো মেথড সেট করতে পারবেন না।
  • হেডারের উপর সীমিত নিয়ন্ত্রণ। Content-Type পেলোডের ধরন থেকে অনুমান করা হয় (উদাহরণস্বরূপ, একটি Blob আপনাকে এটিকে প্রভাবিত করতে দেয়, কিন্তু আপনি স্বাধীনভাবে কাস্টম রিকোয়েস্ট হেডার সেট করতে পারবেন না)।
  • পেলোডগুলো ইউজার এজেন্টের একটি ডেটা সীমার মধ্যে গণনা হয়; অতিরিক্ত বড় বিকন false ফেরত দেয়।

কুকিহীন অ্যানালিটিক্সের জন্য, এটি সাধারণত যথেষ্ট — অনেক ট্র্যাকার ঠিক এটাই বাস্তবায়ন করে এবং আর কখনো পিছনে ফিরে তাকায় না। Monoid একটি নির্দিষ্ট কারণে তা করে না: sendBeacon mode: 'cors' প্রকাশ করতে পারে না, এবং Monoid-এর ইনস্টল স্নিপেট একটি সাইটকে হোস্ট পেজ থেকে ভিন্ন একটি অরিজিনে data-api-url নির্দেশ করতে দেয়। এই একটি সীমাবদ্ধতাই নিচের তুলনাটি নির্ধারণ করে।

keepalive সহ fetch

Fetch স্ট্যান্ডার্ড একটি keepalive ফ্ল্যাগ সংজ্ঞায়িত করে যা একটি রিকোয়েস্টকে সেটি চালু করা পেজের চেয়েও বেশি সময় বাঁচিয়ে রাখে, যা আপনাকে আরও সমৃদ্ধ API সহ sendBeacon-এর মতো স্থায়িত্ব দেয়।

fetch('/collect', {
  method: 'POST',
  keepalive: true,
  headers: { 'Content-Type': 'application/json' },
  body: payload,
});

এর শক্তিশালী দিকগুলো:

  • মেথড, হেডার এবং বডির উপর সম্পূর্ণ নিয়ন্ত্রণ।
  • একটি প্রকৃত Response যা আপনি পরীক্ষা করতে পারেন (যদিও আনলোডের সময় প্রায়শই এটির জন্য অপেক্ষা করা উচিত নয়)।

সীমাবদ্ধতাগুলো:

  • Fetch স্পেসিফিকেশন একটি ডকুমেন্টে চলমান সব keepalive রিকোয়েস্টের মোট বডির আকার 64 KiB-তে সীমাবদ্ধ করে। এই সীমা অতিক্রম করলে fetch প্রত্যাখ্যাত হয়। এটি ডকুমেন্ট-প্রতি একটি বাজেট, তাই একাধিক সমসাময়িক keepalive রিকোয়েস্ট এটি ভাগ করে নেয়।
  • ঐতিহাসিকভাবে, keepalive-এর সাপোর্ট sendBeacon-এর চেয়ে পিছিয়ে ছিল, এবং এতে কিছু ইমপ্লিমেন্টেশন-সংক্রান্ত অদ্ভুততাও দেখা গেছে। আপনার দর্শকরা আসলে যে ব্রাউজারগুলো ব্যবহার করেন সেগুলোতেই পরীক্ষা করুন।

Monoid-এর ট্র্যাকার প্রতিটি রিকোয়েস্টের জন্য ঠিক এটিই ব্যবহার করে — /collect, /duration এবং /event সবক্ষেত্রেই সমানভাবে:

fetch(endpoint, {
  method: 'POST',
  body: JSON.stringify(payload),
  headers: { 'Content-Type': 'application/json' },
  keepalive: true,
  mode: 'cors',
});

আগের সেকশনের নির্ণায়ক বিষয়টি হলো mode: 'cors', পেলোডের আকার বা হেডারের সংখ্যা নয় — এটি মনে রাখার মতো, কারণ বেশিরভাগ লেখাই এই পছন্দটিকে শুধু আকারের নিরিখেই তুলে ধরে।

কোনটি বেছে নেবেন

একটি বাস্তবসম্মত নিয়ম: ফায়ার-অ্যান্ড-ফরগেট টেলিমেট্রির জন্য sendBeacon ব্যবহার করুন, যেখানে আপনার ফিরতি কিছুরই দরকার নেই, এবং fetch keepalive-এর দিকে তখনই যান যখন আপনার সত্যিকারভাবেই কাস্টম হেডার, একটি বড় স্ট্রাকচার্ড রিকোয়েস্ট, বা ক্রস-অরিজিন mode নিয়ন্ত্রণের প্রয়োজন হয়। যেভাবেই হোক পেলোড ছোট রাখুন — keepalive-এর 64 KiB বাজেটের অনেক নিচে — কারণ অ্যানালিটিক্স ইভেন্টগুলো ডিজাইন অনুসারেই ন্যূনতম হওয়া উচিত। ডেটা মিনিমাইজেশন শুধু একটি কমপ্লায়েন্স অবস্থান নয়; এটিই সেই জিনিস যা আনলোডের সময় নির্ভরযোগ্য ডেলিভারিকে সম্ভব করে তোলে। Monoid হলো সেই ব্যতিক্রম যা নিয়মটি আগে থেকেই বিবেচনায় রাখে: পেলোড ছোট এবং রেসপন্স কখনোই পড়া হয় না — ঠিক যে প্রোফাইলের জন্য sendBeacon তৈরি করা হয়েছিল — কিন্তু mode: 'cors'-এর সাপোর্টই শেষ পর্যন্ত জয়ী হয়।

কোন ইভেন্টটি শোনা উচিত

unload-এর উপর নির্ভর করবেন না। এটি নির্ভরযোগ্য নয় এবং ব্যাক/ফরোয়ার্ড ক্যাশ (bfcache) নষ্ট করে দেয়। Page Lifecycle-এর নির্দেশিকা visibilitychange শোনার এবং hidden-এ রূপান্তরকে ডেটা ফ্লাশ করার শেষ নির্ভরযোগ্য মুহূর্ত হিসেবে বিবেচনা করার পরামর্শ দেয়। এটি ডেস্কটপে বন্ধ করা, ট্যাব সুইচ করা এবং মোবাইলে অ্যাপ ব্যাকগ্রাউন্ডিং — এই সব ক্ষেত্রেই সামঞ্জস্যপূর্ণভাবে কাজ করে, যেখানে unload কখনোই ট্রিগার হয় না।

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') flush();
});

Monoid-এর ট্র্যাকার এই প্যাটার্নটিই অনুসরণ করে: একটি visibilitychange লিসেনার একটি beforeunload ফলব্যাকের পাশাপাশি থাকে, যাতে ট্যাবটি মোবাইলে ব্যাকগ্রাউন্ডে পাঠানো হোক বা ডেস্কটপে সম্পূর্ণভাবে বন্ধ করা হোক — উভয় ক্ষেত্রেই একটি ডিউরেশন স্যাম্পল রিপোর্ট করা হয়।

এটি কুকিহীন মডেলের সাথে কীভাবে মানানসই

যেহেতু Monoid কখনো কুকি বা স্টোরেজ পড়ে না এবং কখনো ফিঙ্গারপ্রিন্টিং করে না, প্রতিটি রিকোয়েস্ট কেবল সেটুকুই বহন করে যা এমনিতেই প্রকাশ পায় — একটি স্থূল ইভেন্ট, সেই সাথে ব্রাউজার এমনিতেই পাঠায় এমন হেডার। এমন কোনো আইডেন্টিফায়ার নেই যা ধরে রাখতে হবে, তাই স্টেট সিঙ্ক করার জন্য আমাদের কখনোই দ্বিমুখী এক্সচেঞ্জের দরকার হয় না। এটাই সেই কারণ যা mode: 'cors'-এর কারণে sendBeacon বাদ পড়ার সাথে সাথেই fetch keepalive-কে সহজ পছন্দে পরিণত করে: একটি ওয়ান-শট POST যার রেসপন্স আমরা কখনো পরীক্ষা করি না, তা আমাদের জন্য কোনো সীমাবদ্ধতা নয়; বরং এটিই ডেলিভারির নির্ভরযোগ্যতাকে প্রাইভেসি বাই ডিজাইনের সাথে সামঞ্জস্যপূর্ণ করে তোলে।

Sources

Comments

Loading comments…