6 মিনিট

অবজেক্ট স্টোরেজ বনাম ডাটাবেস ব্লবস: দ্রুত ও সস্তায় আপলোড পরিচালনা

অবজেক্ট স্টোরেজ বনাম ডাটাবেস ব্লব: মেটাডেটা Postgres-এ রাখুন, বাইটগুলো অবজেক্ট স্টোরেজে রাখুন, এবং ডাউনলোড দ্রুত ও খরচ পূর্বানুমানযোগ্য রাখুন।

অবজেক্ট স্টোরেজ বনাম ডাটাবেস ব্লবস: দ্রুত ও সস্তায় আপলোড পরিচালনা

ব্যবহারকারীর আপলোড নিয়ে প্রকৃত সমস্যা

ব্যবহারকারীর আপলোডটি শুনতে সাধারণ: একটি ফাইল নিন, সেভ করুন, পরে দেখান। এটা কয়েকজন ব্যবহারকারী এবং ছোট ফাইলের জন্য কাজ করে। কিন্তু ভলিউম বাড়লে, ফাইল বড় হলে, এবং সমস্যা দেখা দেয় এমন জায়গাগুলোতে যা আপলোড বাটনের সাথে সরাসরি সম্পর্কিত নয়।

ডাউনলোড ধীর হয়ে যায় কারণ আপনার অ্যাপ সার্ভার বা ডাটাবেস ভারি কাজ করছে। ব্যাকআপ বড় ও ধীর হয়, ফলে রিস্টোর করতে বেশি সময় লাগে ঠিক তখন যখন প্রয়োজনে দ্রুত রিস্টোর দরকার। স্টোরেজ বিল ও ব্যান্ডউইথ (ইগ্রেস) বিল বাড়তে পারে কারণ ফাইলগুলো অদক্ষভাবে সার্ভ হচ্ছিল, বহুলপ্রতি-সংস্করণ ছিল, বা ঠিকমতো ক্লিনআপ করা হচ্ছিল না।

আপনি সাধারণত যা চান তা বোরিং এবং নির্ভরযোগ্য: লোডের সময় দ্রুত ট্রান্সফার, স্পষ্ট অ্যাক্সেস নিয়ম, সিম্পল অপারেশন (ব্যাকআপ, রিস্টোর, ক্লিনআপ), এবং ব্যবহার বাড়লেও খরচ পূর্বানুমানযোগ্য থাকা।

সেখানে পৌঁছাতে, দুইটি জিনিস আলাদা করুন যেগুলো প্রায়ই একসাথে মিশে যায়:

মেটাডেটা হল ফাইল সম্পর্কে ছোট তথ্য: কে এর মালিক, কী নামে আছে, সাইজ, টাইপ, কখন আপলোড করা হয়েছে, এবং কোথায় আছে। এটি আপনার ডাটাবেসে (যেমন Postgres) থাকা উচিত কারণ আপনাকে এটিকে কোয়ারি করতে, ফিল্টার করতে, এবং ইউজার/প্রজেক্ট/পারমিশনের সাথে যোগ করতে হবে।

ফাইল বাইট হল ফাইলের আসল কনটেন্ট (ফটো, PDF, ভিডিও)। ডাটাবেস ব্লবের মধ্যে বাইট রাখা কাজ করতে পারে, কিন্তু এতে ডাটাবেস ভারী হয়ে যায়, ব্যাকআপ বড় হয়, এবং পারফরম্যান্স পূর্বাভাস করা কঠিন হয়। বাইটগুলো অবজেক্ট স্টোরেজে রাখা ডাটাবেসকে তার মূল কাজেই রাখে, আর ফাইলগুলো দ্রুত এবং সস্তায় সার্ভ হয়—এসব সিস্টেমই এ কাজের জন্য তৈরী।

অবজেক্ট স্টোরেজ বনাম ডাটাবেস ব্লব, সহজ ভাষায়

যখন কেউ বলে “আপলোড ডাটাবেসে রাখুন,” তারা সাধারণত ডাটাবেস ব্লবগুলো বোঝায়: বা একটি BYTEA কলাম (রো-তে কাঁচা বাইট) অথবা Postgres "large objects" (বড় ভ্যালুগুলো আলাদাভাবে রাখে) ব্যবহার। উভয়ই কাজ করতে পারে, কিন্তু উভয়ই আপনার ডাটাবেসকে ফাইল বাইট সার্ভ করার দায়িত্ব দেয়।

অবজেক্ট স্টোরেজ হলো একটি ভিন্ন ধারণা: ফাইলটি একটি বাকেটে অবজেক্ট হিসেবে থাকে, একটি কী দিয়ে অ্যাড্রেস করা হয় (যেমন uploads/2026/01/file.pdf)। এটি বড় ফাইল, সস্তা স্টোরেজ, এবং স্ট্রিমিং ডাউনলোডের জন্য তৈরি। এটি অনেক সঙ্গোপন-পাঠও ভালভাবে হ্যান্ডেল করে, আপনার ডাটাবেস কানেকশান আটকে না রেখে।

Postgres কোয়েরি, কনস্ট্রেইন্ট, এবং ট্রানজেকশন-এ জ্বলে ওঠে। এটি সেই মেটাডেটার জন্য দুর্দান্ত—কে ফাইলের মালিক, কি টাইপ, কখন আপলোড হয়েছে, এবং ডাউনলোড করা যাবে কি না। মেটাডেটা ছোট, ইনডেক্স করা সহজ, এবং কনসিস্টেন্ট রাখা সহজ।

একটি ব্যবহারিক নিয়ম:

  • ফাইল মেটাডেটা, পারমিশন, এবং সম্পর্কের জন্য Postgres ব্যবহার করুন।
  • ফাইলগুলো কয়েক MB এর বাইরে গেলে বা ডাউনলোড ঘনঘন হলে বাইটগুলো অবজেক্ট স্টোরেজে রাখুন।
  • ছোট অ্যাসেটের জন্য (যেমন আইকন) এবং আপনি নিশ্চিত যে ডাটাবেস বাড়বে না—তখনই DB ব্লব বিবেচনা করুন।

একটি দ্রুত সেনস চেক: যদি ব্যাকআপ, রেপ্লিকা, এবং মাইগ্রেশনগুলো ফাইল বাইটসহ কঠিন হয়ে যাবে, তাহলে বাইটগুলো Postgres-এর বাইরে রাখুন।

সরল কিন্তু ম্যানেজেবল আর্কিটেকচার

অধিকাংশ টিম যে সেটআপে পৌঁছায় তা সরল: বাইটগুলো অবজেক্ট স্টোরেজে রাখুন, এবং ফাইল রেকর্ড (কে মালিক, কি তা, কোথায় থাকে) Postgres-এ রাখুন। আপনার API সমন্বয় এবং অনুমোদন করে, কিন্তু বড় আপলোড বা ডাউনলোড প্রোক্সি করে না।

এটি আপনাকে তিনটি স্পষ্ট দায়িত্ব দেয়:

  • Postgres প্রতিটি ফাইলের জন্য একটি ছোট রো রাখে: একটি স্থিতিশীল file_id, owner, সাইজ, content type, এবং অবজেক্ট পয়েন্টার।
  • অবজেক্ট স্টোরেজ আসল বাইট রাখে, বড় ফাইলের জন্য অপ্টিমাইজ করা এবং সস্তা স্টোরেজ দেয়।
  • আপনার API ফাইল রেকর্ড তৈরি ও অনুমোদন করে, এবং সংক্ষিপ্ত-মেয়াদী স্টোরেজ অনুমতি দেয়।

ওই স্থিতিশীল file_id সব কিছুই প্রধান কী হয়ে উঠে: অ্যাটাচমেন্ট রেফারেন্স করা মন্তব্য, একটি PDF-কে নির্দেশ করা ইনভয়েস, অডিট লগ, এবং সাপোর্ট টুল। ব্যবহারকারী ফাইলের নাম পরিবর্তন করতে পারে, আপনি বাকেট বদলে দিতে পারেন, কিন্তু file_id অপরিবর্তিত থাকে।

সম্ভব হলে, সংরক্ষিত অবজেক্টগুলোকে অপরিবর্তনীয় হিসেবে বিবেচনা করুন। যদি ব্যবহারকারী একটি ডকুমেন্ট প্রতিস্থাপন করে, স্থানে ওভাররাইট করার বদলে একটি নতুন অবজেক্ট (এবং সাধারণত একটি নতুন রো বা ভersion রো) তৈরি করুন। এতে ক্যাশিং সহজ হয়, "পুরাতন লিঙ্ক নতুন ফাইল দেখায়" ধরনের বিস্ময় রোধ হয়, এবং ক্লিয়ার রোলব্যাক কাহিনী থাকে।

প্রাইভেসি আগে থেকেই ঠিক করুন: ডিফল্টভাবে প্রাইভেট, কীভাবে পাবলিক হবে তা ব্যতিক্রম হিসেবে রাখুন। একটি ভালো নিয়ম হলো: ডাটাবেস কে ফাইল অ্যাক্সেস করতে পারে তা সর্বোচ্চ সত্যি হিসেবে ধরে; অবজেক্ট স্টোরেজ আপনার API যে সংক্ষিপ্ত-মেয়াদী অনুমতি দেয় তা প্রয়োগ করে।

Postgres-এ ফাইল মেটাডেটা কিভাবে মডেল করবেন

পরিষ্কার স্প্লিটের সঙ্গে, Postgres ফাইল সম্পর্কে তথ্য রাখে, আর অবজেক্ট স্টোরেজ বাইট রাখে। এতে আপনার ডাটাবেস ছোট থাকে, ব্যাকআপ দ্রুত হয়, এবং কোয়েরি সিম্পল থাকে।

একটি বাস্তবসম্মত uploads টেবিলে কয়েকটি ফিল্ডই যথেষ্ট যেগুলো সত্যিই জিজ্ঞাসা করবে যেমন "এটি কার?", "কোথায় সংরক্ষিত?", এবং "এটি ডাউনলোড করা নিরাপদ?"।

CREATE TABLE uploads (
  id               uuid PRIMARY KEY,
  owner_id         uuid NOT NULL,
  bucket           text NOT NULL,
  object_key       text NOT NULL,
  size_bytes       bigint NOT NULL,
  content_type     text,
  original_filename text,
  checksum         text,
  state            text NOT NULL CHECK (state IN ('pending','uploaded','failed','deleted')),
  created_at       timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX uploads_owner_created_idx ON uploads (owner_id, created_at DESC);
CREATE INDEX uploads_checksum_idx ON uploads (checksum);

কিছু সিদ্ধান্ত যা পরে দুর্দশা রোধ করে:

  • স্টোরেজ পয়েন্টার হিসেবে bucket + object_key ব্যবহার করুন। আপলোডের পর এটা অপরিবর্তনীয় রাখুন।
  • স্টেট ট্র্যাক করুন। যখন ব্যবহারকারী একটি আপলোড শুরু করে, একটি pending রো ইনসার্ট করুন। শুধুমাত্র আপনার সিস্টেম অবজেক্টটি আছে এবং সাইজ (এবং আদর্শত চেকসাম) মিললে uploaded হিসেবে ফ্লিপ করুন।
  • original_filename কেবল প্রদর্শনের জন্য রাখুন। টাইপ বা সিকিউরিটি সিদ্ধান্তে এটিকে বিশ্বাস করবেন না।

আপনি যদি রিপ্লেসমেন্ট সমর্থন করেন (যেমন ব্যবহারকারী একটি ইনভয়েস পুনরায় আপলোড করছে), তাহলে একটি আলাদা upload_versions টেবিল যোগ করুন যার মধ্যে upload_id, version, object_key, এবং created_at থাকবে। এতে ইতিহাস থাকবে, ভুলগুলো রোলব্যাক করা সহজ হবে, এবং পুরনো রেফারেন্স ভেঙে পড়বে না।

আপলোড ফ্লো ধাপে ধাপে (আপনার API ব্লক করতে দেবেন না)

আপলোডগুলো দ্রুত রাখুন API-কে সমন্বয় করানোর মাধ্যমে, বাইট হ্যান্ডল করানোর মাধ্যমে নয়। আপনার ডাটাবেস প্রতিক্রিয়াশীল থাকে, আর অবজেক্ট স্টোরেজ ব্যান্ডউইথ হিট নেয়।

শুরুতে আপলোড রেকর্ড তৈরি করুন—ক্লায়েন্ট কোন স্থানে আপলোড করবে তার upload_id, object_key, এবং সংক্ষিপ্ত-মেয়াদী আপলোড অনুমতি API থেকে ফিরে পাবে।

একটি সাধারণ ফ্লো:

  1. ক্লায়েন্ট আপলোড চাইলো: আপনার API একটি pending রো তৈরি করে, প্রত্যাশিত সাইজ ও ইচ্ছুক content type সহ।
  2. API একটি প্রিসাইনড URL রিটার্ন করে: বড় ফাইলের জন্য প্রিসাইনড আপলোড URL জেনারেট করুন। ছোট ফাইলের (যেমন অ্যাভাটার) জন্য যদি ক্লায়েন্ট-সাইড সাদাসিধে কোড চান, তখন ব্যাকএন্ড মারফৎ প্রোক্সি করা যায়।
  3. ক্লায়েন্ট সরাসরি অবজেক্ট স্টোরেজে আপলোড করে: ব্রাউজার বা মোবাইল অ্যাপ বাইটগুলো স্টোরেজে পাঠায়, আপনার API-র মধ্য দিয়ে নয়।
  4. ফাইনালাইজ: ক্লায়েন্ট upload_id এবং স্টোরেজ রেসপন্সের ক্ষেত্র (যেমন ETag) দিয়ে আপনার API-কে কল করে। সার্ভার সাইজ, চেকসাম (যদি ব্যবহার করেন), এবং content type যাচাই করে, তারপর রোকে uploaded হিসেবে মার্ক করে।
  5. নিরাপদভাবে ব্যর্থ করুন: যাচাই ফেল করলে failed মার্ক করুন এবং ঐ অবজেক্ট মুছে দেওয়ার অপশন রাখুন।

রিট্রাই এবং ডুপ্লিকেট স্বাভাবিক। ফাইনালাইজ কলকে idempotent রাখুন: একই upload_id আবার ফাইনালাইজ করলে সাফল্য রিটার্ন করুন এবং কিছু বদলাবেন না।

রিট্রাই ও রি-আপলোডের কারণে ডুপ্লিকেট কমানোর জন্য একটি চেকসাম সংরক্ষণ করুন এবং "একই মালিক + একই চেকসাম + একই সাইজ" বিষয়টিকে একই ফাইল হিসেবে বিবেচনা করুন।

ডাউনলোড ফ্লো ধাপে ধাপে (দ্রুত ও ক্যাশ-ফ্রেন্ডলি)

Ship private downloads
Add a stable file_id gate endpoint and issue short-lived download access.

একটি ভালো ডাউনলোড ফ্লো একটি স্থির URL দিয়ে শুরু করা উচিত, যদিও বাইটগুলো অন্যত্র থাকতে পারে। ভাবুন: /files/{file_id}। আপনার API file_id দেখে Postgres-এ মেটাডেটা দেখে, পারমিশন চেক করে, তারপর ডেলিভারির উপায় ঠিক করে।

  1. ক্লায়েন্ট আপনার স্থির URL file_id দিয়ে রিকোয়েস্ট করে।
  2. API যাচাই করে যে ব্যবহারকারী অ্যাক্সেস পাবে এবং ফাইলটি uploaded আছে।
  3. API বা তো অবজেক্ট স্টোরেজ-এ রিডাইরেক্ট দেয় (ঐটুকুই ভালো), অথবা প্রাইভেট ফাইলের জন্য সংক্ষিপ্ত-মেয়াদী প্রিসাইনড GET URL রিটার্ন করে।
  4. ক্লায়েন্ট সরাসরি অবজেক্ট স্টোরেজ থেকে ডাউনলোড করে, আপনার API ও অ্যাপ সার্ভারকে হট-পথ থেকে বাইরে রাখে।

পাবলিক বা সেমি-পাবলিক ফাইলের জন্য রিডাইরেক্ট সহজ এবং দ্রুত। প্রাইভেট ফাইলের জন্য প্রিসাইনড GET URL স্টোরেজকে প্রাইভেট রাখে এবং ব্রাউজারকে সরাসরি ডাউনলোড করতে দেয়।

ভিডিও ও বড় ডাউনলোডের জন্য নিশ্চিত করুন যে আপনার অবজেক্ট স্টোরেজ (এবং যেকোন প্রক্সি লেয়ার) রেঞ্জ রিকোয়েস্ট (Range হেডার) সমর্থন করে। এটি সিকিং ও রিজিউমেবল ডাউনলোড সক্ষম করে। যদি আপনি বাইটগুলো API-র মাধ্যমে ফানেল করেন, তাহলে রেঞ্জ সাপোর্ট প্রায়ই ভেঙে যায় বা ব্যয়বহুল হয়।

ক্যাশিংই গতি নিয়ে আসে। আপনার স্থির /files/{file_id} এন্ডপয়েন্ট সাধারণত নন-ক্যাশেবল হওয়া উচিত (এটি একটি অথ গেট), যেখানে অবজেক্ট স্টোরেজ রেসপন্স কন্টেন্টের ভিত্তিতে ক্যাশ করা যেতে পারে। ফাইলগুলো যদি অপরিবর্তনীয় (নতুন আপলোড = নতুন কী) হয়, তাহলে দীর্ঘ ক্যাশ লাইফ সেট করতে পারেন। ওভাররাইট করলে ক্যাশ সময় কম রাখুন বা ভার্সনড কী ব্যবহার করুন।

আপনি যদি বিশ্বজুড়ে অনেক ব্যবহারকারী বা বড় ফাইল পান তাহলে CDN সাহায্য করবে। আপনার দর্শক সংখ্যাটা কম বা এক অঞ্চলে হলে অবজেক্ট স্টোরেজই প্রায়ই যথেষ্ট এবং শুরুতে সস্তা হয়।

সময়ের সাথে খরচ পূর্বানুমানযোগ্য রাখা

অপ্রত্যাশিত বিল সাধারণত ডাউনলোড এবং চর্ন থেকে আসে, কাঁচা বাইট ডাঙায় নেই।

চারটি ড্রাইভার মূল্য নির্ধারণ করে: আপনি কত স্টোর করেন, কত ঘনঘন পড়া/লিখা হয় (রিকোয়েস্ট), কত ডেটা প্রোভাইডার থেকে বাইরে যায় (ইগ্রেস), এবং আপনি কি CDN ব্যবহার করেন বারবার অরিজিন ডাউনলোড কমাতে। একটি ছোট ফাইল 10,000 বার ডাউনলোড হলে সেটি কখনোই কারো স্পর্শে না থাকা বড় ফাইলের মত ব্যয়বহুল হতে পারে।

নিয়ন্ত্রণ যা ব্যয়ের নিয়ন্ত্রণ রাখে:

  • আপলোড প্রতি ফাইল সাইজ ক্যাপ এবং ব্যবহারকারীর কোটা নির্ধারণ করুন।
  • অপব্যবহার ও দুর্ঘটনাজনিত লুপ এড়াতে আপলোড ও ডাউনলোড রেট লিমিট করুন।
  • লাইফসাইকেল রুল ব্যবহার করুন যাতে পুরনো ফাইলগুলো সস্তা স্তরে মুভ করে বা অপ্রয়োজনীয় হলে মুছে দেয়।
  • চেকসাম দিয়ে ডিডুপ্লিকেট করুন যাতে রিট্রাই বা রি-আপলোড অতিরিক্ত কপি তৈরি না করে।
  • বিলিং ও সতর্কতার জন্য Postgres-এ ইউজেজ কাউন্টার স্টোর করুন—তথ্যভিত্তিক সিদ্ধান্ত নিতে সহায়তা করে।

লাইফসাইকেল রুলগুলো প্রায়ই সবচেয়ে সহজ জয়। উদাহরণস্বরূপ: মূল ফটো 30 দিন "হট" রাখবেন, তারপর সস্তা স্টোরেজ ক্লাসে পাঠিয়ে দিন; ইনভয়েস 7 বছর রাখুন; ব্যর্থ আপলোড পার্টস 7 দিন পর মুছে দিন। এমন সহজ রিটেনশন নীতি স্টোরেজ বাড়া আটকায়।

ডিডুপ্লিকেশন সরল হতে পারে: একটি কন্টেন্ট হ্যাশ (যেমন SHA-256) ফাইল মেটাডেটা টেবিলে রাখুন এবং প্রতিটি মালিকের জন্য ইউনিকনেস প্রয়োগ করুন। যখন ব্যবহারকারী একই PDF দুবার আপলোড করে, আপনি বিদ্যমান অবজেক্টটি পুন ব্যবহার করতে পারেন এবং কেবল একটি নতুন মেটাডেটা রো তৈরি করতে পারেন।

সবশেষে, যেখানে আপনি ইতিমধ্যেই ব্যবহারকারীর হিসাব রাখেন সেখানে Usage ট্র্যাক করুন: Postgres-এ প্রতি ব্যবহারকারী বা ওয়ার্কস্পেসের জন্য bytes_uploaded, bytes_downloaded, object_count, এবং last_activity_at রাখুন। এতে UI-তে কোটা দেখানো ও বিলিং আগেই সতর্কতা দেওয়া সহজ হয়।

আপলোডের জন্য সিকিউরিটি ও কমপ্লায়েন্সের বেসিকস

Set up presigned uploads
Create direct-to-storage uploads with a finalize step, without proxying file bytes.

আপলোড সিকিউরিটি মূলত দুই জিনিসে আসে: কে ফাইল অ্যাক্সেস করতে পারে, এবং কিছু ভুল হলে আপনার কাছে পরে কী প্রমাণ থাকবে।

বাস্তব দাবি মিলানো অ্যাক্সেস কন্ট্রোল

স্পষ্ট এক্সেস মডেল দিয়ে শুরু করুন এবং এটি Postgres মেটাডেটায় এনকোড করুন—একটিপথে সার্ভিস জুড়ে ছড়িয়ে থাকা এক-অফ নিয়ম নয়।

অধিকাংশ অ্যাপ কভার করে এমন একটি সাধারণ মডেল:

  • মালিক-শুধু: কেবল আপলোডকারী (এবং অ্যাডমিন) অ্যাক্সেস করতে পারে।
  • শেয়ারড: নির্দিষ্ট ব্যবহারকারী বা টিম/ওয়ার্কস্পেস অ্যাক্সেস করতে পারে।
  • পাবলিক: লগইন ছাড়াই অ্যাক্সেসযোগ্য (সংযতভাবে ব্যবহার করুন এবং এটাও ট্র্যাক করুন)।

প্রাইভেট ফাইলের জন্য কাঁচা অবজেক্ট কী এক্সপোজ করা এড়ান। সময়-সীমিত, স্কোপ-সীমিত প্রিসাইনড আপলোড ও ডাউনলোড URL ইস্যু করুন এবং সেগুলো প্রায়ই রোটেট করুন।

পরে কাজে লাগবে এমন কমপ্লায়েন্স চেক

ট্রানজিটে এবং রেস্টে এনক্রিপশন নিশ্চিত করুন। ট্রানজিটে মানে সারাবিশ্ব HTTPS, অন্তর্ভুক্ত করে স্টোরেজে সরাসরি আপলোড। রেস্টে মানে আপনার স্টোরেজ প্রোভাইডারের সার্ভার-সাইড এনক্রিপশন, এবং ব্যাকআপ ও রেপ্লিকাও এনক্রিপ্টেড আছে তা নিশ্চিত করুন।

সুরক্ষা ও ডেটা কোয়ালিটির জন্য চেকপয়েন্ট যোগ করুন: আপলোড URL ইস্যু করার আগে টাইপ ও সাইজ ভ্যালিডেট করুন, তারপর আপলোডের পরে আবার ভ্যালিডেট করুন (বস্তুত স্টোর্ড বাইটের উপর ভিত্তি করে, শুধুমাত্র ফাইলনেম নয়)। আপনার রিস্ক প্রোফাইল প্রয়োজনে মালওয়্যার স্ক্যানিং অ্যাসিঙ্ক্রোনাসভাবে চালান এবং ফাইলটি পাশ না করা পর্যন্ত কোয়ারেন্টাইন করুন।

ঘটনা অনুসন্ধানের জন্য অডিট ফিল্ড রাখুন: uploaded_by, ip, user_agent, এবং last_accessed_at একটি বাস্তবসম্মত বেসলাইন।

ডেটা রেসিডেন্সি প্রয়োজন থাকলে স্টোরেজ রিজিয়ন সচেতনভাবে নির্বাচন করুন এবং এটি আপনার কম্পিউট পরিবেশের সাথে সামঞ্জস্য রাখুন।

ধীরতা ও ইনসিডেন্টের সাধারণ ভুলগুলো

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

  • ফাইল বাইট Postgres-এর ভিতরে রাখা: ছোট অ্যাপের জন্য কাজ করে, তারপর ব্যাকআপ বাড়ে, রিস্টোর দীর্ঘ হয়, এবং রুটিন মেইনটেন্যান্স ঝুঁকিপূর্ণ হয়ে ওঠে। একটি বড় টেবিল ভ্যাকুয়াম, রেপ্লিকেশন, এমনকি সাধারণ কুয়েরিকেও ধীর করে দিতে পারে।
  • ব্যবহারকারী-দেওয়া ফাইলনামকে অবজেক্ট কী হিসেবে ব্যবহার করা: সংঘর্ষ ঘটে (দুই ব্যবহারকারী invoice.pdf আপলোড করে), এবং অদ্ভুত ক্যারেক্টার এজ-কেস তৈরি করে। দেখানোর জন্য মূল নাম রাখুন, কিন্তু স্টোরেজের জন্য ইউনিক কী জেনারেট করুন।
  • ফাইনালাইজ সময় যাচাই স্কিপ করা: ক্লায়েন্ট-সাইডে যাচাই করলেও, সার্ভার-সাইডে সাইজ, কনটেন্ট টাইপ, এবং মালিকানার যাচাই করা দরকার যখন আপনি আপলোডকে সম্পন্ন হিসেবে চিহ্নিত করেন।
  • অপচয়বশত অবজেক্ট পাবলিক করা ও অ্যাক্সেস রোটেশন না করা: একটি "অস্থায়ী" পাবলিক বালতি পলিসি বা দীর্ঘ সময়ের URL প্রায়শই স্থায়ী হয়ে যায়। সংক্ষিপ্ত-মেয়াদী ডাউনলোড লিঙ্ক পREFER করুন, এবং দ্রুত রিভোক করার উপায় রাখুন।
  • শুধু এক পাশ মুছে ফেলা (মেটাডেটা বা বাইট): Postgres রো ডিলিট করে কিন্তু অবজেক্ট রেখে দিলে নীরব খরচ সৃষ্টি হয়। অবজেক্ট ডিলিট করে কিন্তু মেটাডেটা রেখে দিলে ব্রোকেন ডাউনলোড এবং সাপোর্ট লোড তৈরি হয়।

একটি কনক্রিট উদাহরণ: ব্যবহারকারী যদি প্রোফাইল ফটো তিনবার প্রতিস্থাপন করে, আপনি তিনটি পুরনো অবজেক্টের জন্যই অর্থ দিতে পারেন যতক্ষণ না আপনি ক্লিনআপ ঠিক করে রাখেন। একটি নিরাপদ প্যাটার্ন হলো Postgres-এ সফট ডিলিট, তারপর ব্যাকগ্রাউন্ড জব অবজেক্ট মুছে ফেলা এবং ফলাফল রেকর্ড করা।

প্রি-লঞ্চ দ্রুত চেকলিস্ট

প্রথম বড় ফাইল আসলে, একটি ব্যবহারকারী আপলোডের মাঝখানে পেজ রিফ্রেশ করলে, বা কেউ অ্যাকাউন্ট ডিলিট করলে বাইটগুলো পিছনে থেকে যাওয়ার সময়ই বেশিরভাগ সমস্যা দেখা দেয়।

নিশ্চিত করুন আপনার Postgres টেবিল ফাইলের সাইজ, চেকসাম (ইন্টিগ্রিটি যাচাই করতে), এবং একটি স্পষ্ট স্টেট পাথ (যেমন: pending, uploaded, failed, deleted) রেকর্ড করে।

একটি লাস্ট-মাইল চেকলিস্ট:

  • রিট্রাই নিরাপদ কিনা নিশ্চিত করুন: বারবার চেষ্টা করলে অতিরিক্ত অবজেক্ট বা অনুপস্থিত বাইটসহ নতুন uploaded রো তৈরি হওয়া যাবে না।
  • আপলোড রিজিউমেবল বা অন্তত রিস্টার্টেবল রাখুন যাতে টাইমআউট ও মোবাইল নেটওয়ার্ক ইস্যু কম থাকে।
  • ডাউনলোড রেঞ্জ রিকোয়েস্ট হ্যান্ডল করে তা যাচাই করুন যাতে বড় ফাইল দ্রুত শুরু হয় এবং বিরতি পরে পুনরায় চালু করা যায়।
  • ডিলিশনের এন্ড-টু-এন্ড সংজ্ঞা করুন: মেটাডেটা টম্বস্টোন করুন, অবজেক্ট বাইট মুছুন, এবং যদি একটি কাজ ব্যর্থ হয় তবে ডিলে ক্লিনআপ হ্যান্ডেল করুন।
  • বেসিক মনিটরিং যোগ করুন: আপলোড/ডাউনলোড এরর রেট, স্টোরেজ গ্রোথ, এবং হঠাৎ ইগ্রেস স্পাইক।

একটি কনক্রিট টেস্ট: 2 GB ফাইল আপলোড করুন, 30% এ পেজ রিফ্রেশ করুন, তারপর রিজিউম করুন। তারপর ধীর কানেকশনে ডাউনলোড করে মাঝখানে সিক করে দেখুন। যদি দুই ফ্লোই অস্থির হয়, লঞ্চের আগেই ঠিক করুন।

উদাহরণ দৃশ্য: একটি অ্যাপে ফটো এবং ইনভয়েস একসঙ্গে

Add file versioning safely
Draft versioned objects and rollback-friendly keys so replacements do not break links.

একটি সাধারণ SaaS অ্যাপে দুই ধরনের আপলোড থাকে: প্রোফাইল ফটো (ঘনঘন, ছোট, ক্যাশ করা নিরাপদ) এবং PDF ইনভয়েস (সংবেদনশীল, প্রাইভেট রাখতে হবে)। এখানে Postgres-এ মেটাডেটা এবং অবজেক্ট স্টোরেজে বাইট রাখার সুবিধা স্পষ্ট হয়।

একটি files টেবিলের মেটাডেটা এই রকম দেখাতে পারে, কয়েকটি ক্ষেত্রে আচরণ প্রভাবিত করে:

fieldপ্রোফাইল ফটো উদাহরণইনভয়েস PDF উদাহরণ
kindavatarinvoice_pdf
visibilityprivate (সাইনড URL দিয়ে সার্ভ)private
cache_controlpublic, max-age=31536000, immutableno-store
object_keyusers/42/avatars/2026-01-17T120102Z.webporgs/7/invoices/INV-1049.pdf
statusuploadeduploaded
size_bytes184233982341

যখন ব্যবহারকারী একটি ছবি প্রতিস্থাপন করে, এটিকে ওভাররাইট না করে নতুন ফাইল হিসেবে বিবেচনা করুন। একটি নতুন রো ও নতুন object_key তৈরি করুন, তারপর ইউজার প্রোফাইলকে নতুন file_id-তে পয়েন্ট করুন। পুরানো রোকে replaced_by=<new_id> (অথবা deleted_at) দিয়ে মার্ক করুন, এবং ব্যাকগ্রাউন্ড জবের মাধ্যমে পুরানো অবজেক্ট পরে ডিলিট করুন। এতে ইতিহাস থাকে, রোলব্যাক সহজ হয়, এবং রেস কন্ডিশন এড়ায়।

সাপোর্ট ও ডিবাগিং সহজ হয় কারণ মেটাডেটা একটি গল্প বলে। যখন কেউ বলে "আমার আপলোড ব্যর্থ হয়েছে," সাপোর্ট status, একটি মানুষের পড়ার যোগ্য last_error, একটি storage_request_id বা etag (স্টোরেজ লগ ট্রেস করতে), টাইমস্ট্যাম্প (স্টল হয়েছে কি না?), এবং owner_idkind চেক করে (এক্সেস পলিসি ঠিক আছে কি?) দেখতে পারে।

অব্যাহতঃ অতিরিক্ত পদক্ষেপ বহু-নির্মাণ ছাড়াই

ছোটে শুরু করুন এবং হ্যাপি-পাথে বোর্ডিং বোরিং রাখুন: ফাইল আপলোড হয়, মেটাডেটা সেভ হয়, ডাউনলোড দ্রুত হয়, এবং কিছুই হারায় না।

একটি ভাল প্রথম মাইলফলক হলো একটি ন্যূনতম Postgres টেবিল ফাইল মেটাডেটার জন্য প্লাস একটি একক আপলোড ফ্লো এবং একটি একক ডাউনলোড ফ্লো যা আপনি বোর্ডে ব্যাখ্যা করতে পারবেন। তারপর সার্ভার-সাইড ভার্সনিং, কোটা, এবং লাইফসাইকেল নিয়ম যোগ করুন।

প্রতি ফাইল টাইপের জন্য একটি স্পষ্ট স্টোরেজ নীতি নির্বাচন করুন এবং এটিকে লিখে রাখুন। উদাহরণস্বরূপ, প্রোফাইল ফটো ক্যাশেবল হতে পারে, আর ইনভয়েস প্রাইভেট এবং শুধুমাত্র সংক্ষিপ্ত-মেয়াদী ডাউনলোড URL-এর মাধ্যমে অ্যাক্সেসযোগ্য হওয়া উচিত। এক বালতি প্রিফিক্সের মধ্যে নীতিগুলো মিশিয়ে রাখলে দুর্ঘটনাজনিত এক্সপোজার ঘটে।

শুরুতেই মেট্রিক্স যোগ করুন। প্রথম দিন থেকেই যা আপনি চান তা: আপলোড ফাইনালাইজ ফেল রেট, orphan রেট (অবজেক্ট ছাড়া DB রো এবং উল্টো), ফাইল টাইপ অনুযায়ী ইগ্রেস ভলিউম, P95 ডাউনলোড লেটেন্সি, এবং গড় অবজেক্ট সাইজ।

দ্রুত প্রোটোটাইপ করতে চাইলে Koder.ai (koder.ai) এই প্যাটার্ন অনুযায়ী সম্পূর্ণ অ্যাপ জেনারেট করে (React, Go, Postgres), এবং এটি স্কিমা, এন্ডপয়েন্ট, এবং ব্যাকগ্রাউন্ড ক্লিনআপ জবগুলোর উপর দ্রুত পুনরাবৃত্তি করার সহজ উপায় হতে পারে।

তারপর, শুধু সেইগুলো যোগ করুন যেগুলো আপনি এক বাক্যে বলতে পারেন: "আমরা পুরোনো ভার্সন 30 দিন রাখি" বা "প্রতিটি ওয়ার্কস্পেস পাচ্ছে 10 GB." বাস্তব ব্যবহার না ছাড়া জটিল করবেন না।

সাধারণ প্রশ্ন

আমার আপলোড করা ফাইলগুলো Postgres-এ রাখব না কি অবজেক্ট স্টোরেজে?

Postgres-এ সেই মেটাডেটা রাখুন যেটি আপনাকে কোয়ারি ও সুরক্ষিতভাবে রাখতে হবে (মালিক, অনুমতি, স্টেট, চেকসাম, পয়েন্টার)। বাইটগুলো অবজেক্ট স্টোরেজে রাখুন যাতে ডাউনলোড এবং বড় ট্রান্সফার ডাটাবেস কানেকশন খেয়ালে না ফেলে বা ব্যাকআপ বাড়িয়ে না দেয়।

Postgres ব্লব-এ ফাইল বাইট রাখার প্রধান অসুবিধা কী?

এটি ডাটাবেসকে ফাইল সার্ভার হিসেবেও কাজ করতে বাধ্য করে। টেবিলের আকার বাড়ে, ব্যাকআপ ও রিস্টোর ধীর হয়ে যায়, রেপ্লিকেশন লোড বাড়ে, এবং অনেক ব্যবহারকারী একসাথে ডাউনলোড করলে পারফর্ম্যান্স অপ্রত্যাশিতভাবে খারাপ হতে পারে।

কম জটিলভাবে স্কেল করার সবচেয়ে সহজ আপলোড আর্কিটেকচার কী?

হ্যাঁ। অ্যাপে এক স্থায়ী file_id রাখুন, মেটাডেটা Postgres-এ রাখুন, এবং বাইটগুলো অবজেক্ট স্টোরেজে bucketobject_key দিয়ে অ্যাড্রেস করুন। আপনার API অনুমোদন করবে এবং সংক্ষিপ্ত-মেয়াদী আপলোড/ডাউনলোড অনুমতি দেবে—বাইটগুলো প্রোক্সি করবে না।

আমি চাই না যে আমার API ফাইল বাইটগুলো হ্যান্ডল করুক—তাহলে আপলোড ফ্লো কেমন হওয়া উচিত?

প্রথমে একটি pending রেকর্ড তৈরি করুন, একটি ইউনিক object_key জেনারেট করুন, তারপর ক্লায়েন্টকে সংক্ষিপ্ত-মেয়াদী অনুমতি দিয়ে সরাসরি স্টোরেজে আপলোড করতে দিন। আপলোড শেষে ক্লায়েন্ট একটি ফাইনালাইজ কল করবে যাতে সার্ভার আকার ও চেকসাম যাচাই করে এবং তারপর uploaded হিসেবে মার্ক করে।

কেন আমার কাছে `pending`/`uploaded`/`failed`/`deleted` মতো আপলোড “স্টেট” থাকতে হবে?

কারণ বাস্তব আপলোডগুলো ব্যর্থ এবং রিট্রাই করে। একটি স্টেট ফিল্ড আপনাকে এমন ফাইলগুলো আলাদা করতে দেয় যেগুলো প্রত্যাশিত কিন্তু উপস্থিত নেই (pending), সম্পন্ন (uploaded), ত্রুটিপূর্ণ (failed), বা মুছে ফেলা হয়েছে (deleted)—এতে UI, ক্লিনআপ জব, এবং সাপোর্ট টুলগুলো সঠিকভাবে কাজ করে।

কীভাবে স্টোরেজে কী হিসেবে ব্যবহার করব—ব্যবহারকারীর দেওয়া ফাইলনাম নেব কি?

original_filename শুধু প্রদর্শনের জন্য রাখুন। স্টোরেজে সংঘর্ষ, অদ্ভুত ক্যারেক্টার ও নিরাপত্তা সমস্যা এড়াতে ইউনিক সংরক্ষণ কী (অften UUID-ভিত্তিক পাথ) জেনারেট করুন। UI-তে আপনি মূল নাম দেখাতে পারেন, কিন্তু স্টোরেজ পাথগুলো পরিষ্কার ও পূর্বানুমানযোগ্য রাখুন।

প্রাইভেট ফাইলগুলোর জন্য সুপারিশকৃত ডাউনলোড প্যাটার্ন কী?

একটি স্থায়ী অ্যাপ URL (যেমন /files/{file_id}) কে অনুমতি গেট হিসেবে ব্যবহার করুন। Postgres-এ এক্সেস চেক করার পর রিডিরেক্ট দিন অথবা প্রাইভেট ফাইলগুলোর জন্য সংক্ষিপ্ত-মেয়াদী সাইনড GET URL প্রদান করুন যাতে ক্লায়েন্ট সরাসরি অবজেক্ট স্টোরেজ থেকে ডাউনলোড করে এবং আপনার API হট-পাথ থেকে বাইরে থাকে।

ইউজার আপলোডের সাথে সাধারণত কী কারণে হঠাৎ বিল বাড়ে?

ইগ্রেস ও পুনরাবৃত্ত ডাউনলোড সাধারণত প্রধান খরচ চালক; কাচা ডিস্ক স্পেস নয়। ফাইল সাইজ সীমা ও কোটা দিন, রিটেনশন/লাইফসাইকেল নিয়ম ব্যবহার করুন, চেকসাম দিয়ে ডিডুপ্লিকেট করুন যেখানে মানে আছে, এবং ইউজেজ কাউন্টার রাখুন যাতে বিল বাড়ার আগেই সতর্কতা আসতে পারে।

আপলোডের জন্য প্রথম দিনেই কোন কোন সিকিউরিটি চেকগুলো গুরুত্বপূর্ণ?

দিব্যমান নীতি Postgres-এ উৎস হিসেবে রাখুন এবং স্টোরেজকে ডিফল্টভাবে প্রাইভেট রাখুন। আপলোডের আগে ও পরে টাইপ ও সাইজ যাচাই করুন, HTTPS ব্যাবহার করুন, রেস্টে এনক্রিপশন নিশ্চিত করুন, এবং ঘটনার তদন্তের জন্য uploaded_by, ip, user_agent, last_accessed_at মতো অডিট ফিল্ড রাখুন।

এটি দ্রুত বাস্তবায়ন করতে গেলে কোথা থেকেই শুরু করব যাতে ওভারবিল্ড না হয়?

একটি মেটাডেটা টেবিল, একটি ডাইরেক্ট-টু-স্টোরেজ আপলোড ফ্লো, এবং একটি ডাউনলোড গেট এন্ডপয়েন্ট দিয়ে শুরু করুন; তারপর orphan অবজেক্টসমূহ ও সফট-ডিলিট রোদের জন্য ক্লিনআপ জব যোগ করুন। দ্রুত প্রোটোটাইপ করার জন্য Koder.ai (koder.ai) React/Go/Postgres স্ট্যাক থেকে কোড জেনারেট করতে সাহায্য করে, কিন্তু ওভারবিল্ড করবেন না—প্রথমে মৌলিক পথে ঠেকুন।

Related posts