SSD Nodes Learn 8GB RAM — $66/বছর
নির্দেশিকা Matt Connorদ্বারা Matt Connor · আপডেট করা হয়েছে 2026-08-01

VPS-এ প্রোডাকশনে SQLite ব্যবহারের নিয়ম ও সতর্কতা

একটি VPS-এ SQLite ব্যবহারের সুবিধা ও সীমাবদ্ধতা জানুন। WAL mode, busy_timeout এবং Litestream দিয়ে ব্যাকআপ নেওয়ার সঠিক পদ্ধতিসহ কখন এটি ব্যবহার করা উচিত তা এই গাইডে দেখুন।

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, July 30, 2026.

যখন একটি VPS-এ SQLite প্রোডাকশন ডেটাবেস হিসেবে সঠিক পছন্দ

অধিকাংশ ছোট অ্যাপ্লিকেশনের জন্য প্রোডাকশনে SQLite ব্যবহার করা একটি সঠিক সিদ্ধান্ত। এর কারণটি সহজ: একটি মেশিনে একটি প্রসেস যখন একটি ফাইলে ডেটা লেখে, তখন কোনো ডেটাবেস সার্ভারের প্রয়োজন হয় না। এখানে তত্ত্বাবধান করার মতো কোনো daemon নেই, ফায়ারওয়াল করার মতো কোনো port নেই, পরিবর্তন করার মতো কোনো password নেই এবং সচল রাখার মতো দ্বিতীয় কোনো মেশিন নেই। একটি কুয়েরি নেটওয়ার্ক রাউন্ড ট্রিপের পরিবর্তে একটি ফাংশন কল হিসেবে কাজ করে, তাই যে পেজে চল্লিশটি কুয়েরি চলে, তার খরচ মাত্র চল্লিশটি ফাংশন কল।

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

প্রথমে কমান্ড লাইন টুলটি ইনস্টল করুন। নিচের সবকিছু Ubuntu 24.04-এ চালানো হয়েছে।

sudo apt update
sudo apt install -y sqlite3
sqlite3 --version

এটি একটি ভার্সন প্রিন্ট করবে যা 3. দিয়ে শুরু হয় এবং এর পরে একটি বিল্ড তারিখ ও সোর্স হ্যাশ থাকে। জুলাই 2026 অনুযায়ী Ubuntu 24.04-এ SQLite 3.45.1 ভার্সনটি থাকে। আপনার অ্যাপ্লিকেশন সম্ভবত এই বাইনারিটি ব্যবহার করে না: অধিকাংশ ল্যাঙ্গুয়েজ রানটাইম তাদের নিজস্ব SQLite লাইব্রেরি ব্যবহার করে, যা প্রায়শই নতুন ভার্সনের হয়। তাই কোনো নতুন ফিচারের ওপর নির্ভর করার আগে আপনার ডেটাবেস ড্রাইভার যে ভার্সনটি রিপোর্ট করছে তা যাচাই করে নিন।

WAL মোড কেন সবার আগে পরিবর্তন করবেন

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

WAL (write-ahead log) মোড এই ক্রমটিকে উল্টে দেয়। রাইটার নতুন পেজগুলোকে একটি আলাদা -wal ফাইলে যুক্ত করে এবং মূল ডাটাবেসটিকে অপরিবর্তিত রাখে। রিডাররা তাদের শুরু করা স্ন্যাপশট অনুযায়ী মূল ফাইলটি পড়তে থাকে, তাই রিডাররা রাইটারকে ব্লক করে না এবং রাইটারও রিডারদের ব্লক করে না। পরবর্তীতে, একটি চেকপয়েন্ট জমা হওয়া WAL পেজগুলোকে মূল ডাটাবেসে কপি করে দেয়। এই একটি পরিবর্তনই SQLite-কে ওয়েব অ্যাপ্লিকেশনের পেছনে ব্যবহারযোগ্য করে তোলার জন্য যথেষ্ট।

WAL মোড চালু করুন এবং এটি কার্যকর হয়েছে কিনা নিশ্চিত করুন

mkdir -p ~/app
sqlite3 ~/app/app.db "PRAGMA journal_mode=WAL;"

এই কমান্ডটি wal প্রিন্ট করে। এই আউটপুটটি কেবল সাজানোর জন্য নয়। PRAGMA journal_mode ডাটাবেসটি বর্তমানে যে মোডে আছে তা প্রদর্শন করে, তাই যদি delete উত্তর আসে, তবে বুঝতে হবে পরিবর্তনটি ব্যর্থ হয়েছে এবং আপনি এখনও রোলব্যাক জার্নাল মোডেই আছেন।

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

sqlite3 ~/app/app.db "PRAGMA journal_mode;"

এখন একটি টেবিল তৈরি করুন এবং ডিস্কে কী কী ফাইল তৈরি হয়েছে তা দেখুন।

sqlite3 ~/app/app.db <<'SQL'
CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT NOT NULL);
INSERT INTO notes (body) VALUES ('first row');
SQL
ls -l ~/app/

এখন তিনটি ফাইল আছে: app.db, app.db-wal এবং app.db-shm-wal ফাইলটিতে সেইসব কমিট করা পেজ থাকে যা এখনও চেকপয়েন্ট করা হয়নি। -shm ফাইলটি একটি শেয়ার্ড মেমোরি ইনডেক্স, যা প্রতিটি কানেকশন ম্যাপ করে রাখে যাতে তারা সবাই বুঝতে পারে WAL-এ কী আছে। উভয় ফাইলই ডাটাবেসের অংশ এবং এগুলো কোনো অস্থায়ী ফাইল নয়। অ্যাপ্লিকেশন চলাকালীন শুধুমাত্র app.db কপি করলে আপনি এমন একটি ফাইল পাবেন যাতে সাম্প্রতিক সব কমিট অনুপস্থিত থাকবে। app.db ডিলিট করে বাকি দুটি ফাইল রেখে দিলে SQLite সেই পুরনো WAL পেজগুলোকে নতুন ফাইলের ওপর প্রয়োগ করবে, আর এভাবেই মানুষ একটি ডাটাবেস রিসেট করতে গিয়ে সেটিকে নষ্ট করে ফেলে।

প্রোডাকশন অ্যাপের জন্য প্রয়োজনীয় কানেকশন সেটিংস

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

PRAGMA journal_mode = WAL;
PRAGMA busy_timeout = 5000;
PRAGMA synchronous = NORMAL;
PRAGMA foreign_keys = ON;

busy_timeout = 5000 কমান্ডটি SQLite-কে নির্দেশ দেয় যেন কোনো ডেটাবেস লক থাকলে তা সর্বোচ্চ 5000 মিলিসেকেন্ড পর্যন্ত পুনরায় চেষ্টা (retry) করে, এরপর এটি database is locked রিটার্ন করে। ডিফল্ট মান হলো 0, তাই ডিফল্টভাবে SQLite তাৎক্ষণিকভাবে ব্যর্থ হয় যখন দুজন রাইটার একই সময়ে কাজ করার চেষ্টা করে। এই একটি মান সেট করলেই অধিকাংশ লক এরর দূর হয়ে যায়, যা সাধারণত SQLite-এর নিজস্ব সমস্যা বলে ভুল করা হয়।

WAL মোডে synchronous = NORMAL হলো সঠিক সেটিং, এবং এর সুবিধা-অসুবিধা বোঝা জরুরি। FULL মোডে, SQLite প্রতিটি কমিটের সময় WAL-এ fsync কল করে। NORMAL মোডে, এটি চেকপয়েন্টের সময় সিঙ্ক করে। SQLite ডকুমেন্টেশনে স্পষ্টভাবে বলা হয়েছে আপনি কী হারাচ্ছেন: পাওয়ার ফেইলর বা হার্ড রিসেটের পর ট্রানজ্যাকশনগুলো আর টেকসই (durable) থাকে না। পাওয়ার লসের কারণে ডেটাবেস করাপ্ট হবে না, আপনি কেবল সেই শেষ কমিটগুলো হারাবেন যা ডিস্কে পৌঁছায়নি। একটি VPS-এর ক্ষেত্রে সাধারণত এটিই সঠিক সিদ্ধান্ত, কারণ এটি প্রতিটি রাইট অপারেশনের পথ থেকে একটি fsync সরিয়ে ফেলে।

REFERENCES-এর সাথে সামঞ্জস্য বজায় রাখার জন্য foreign_keys = ON ডিফল্টভাবে বন্ধ থাকে এবং এটি প্রতি কানেকশনের জন্য প্রযোজ্য। প্রতিটি কানেকশনে এটি চালু না করা পর্যন্ত REFERENCES ক্লজযুক্ত কোনো স্কিমা আসলে কিছুই কার্যকর করে না।

আরও একটি সেটিং কেবল পরবর্তী সময়ে গুরুত্বপূর্ণ হয়ে ওঠে। WAL যখন 1000 পেজের বেশি বড় হয়, তখন SQLite স্বয়ংক্রিয়ভাবে চেকপয়েন্ট করে এবং সেই মুহূর্তে যে কানেকশনটি ট্রানজ্যাকশন শেষ করে, সেই কানেকশনটিই এই কাজটি সম্পন্ন করে। এটি স্বাভাবিকভাবে ঠিক আছে। কিন্তু যখন Litestream চলমান থাকে, তখন এটি একটি প্রশ্ন হয়ে দাঁড়ায়, কারণ Litestream কখন চেকপয়েন্ট হবে তার নিয়ন্ত্রণ নিজের কাছে রাখতে চায়।

কেন busy_timeout সেট করার পরেও database is locked ঘটে

এটি সেই ব্যর্থতা যা ব্যবহারকারীদের Postgres-এ ফিরে যেতে বাধ্য করে এবং এর একটি সুনির্দিষ্ট কারণ রয়েছে।

একটি busy timeout একটি busy handler ইনস্টল করে, কিন্তু SQLite এটি কল করার কোনো নিশ্চয়তা দেয় না।

যদি SQLite নির্ধারণ করে যে busy handler কল করলে deadlock হতে পারে, তবে এটি busy handler কল না করে সরাসরি অ্যাপ্লিকেশনে SQLITE_BUSY রিটার্ন করবে।

এটি যে deadlock এড়ানোর চেষ্টা করে তা ঘটে যখন একটি ট্রানজ্যাকশন আপগ্রেড হয়। SQLite-এ একটি সাধারণ BEGIN মানে হলো BEGIN DEFERRED। যদি এর পরের প্রথম স্টেটমেন্টটি একটি SELECT হয়, তবে আপনি একটি রিড ট্রানজ্যাকশনে আছেন। যখন সেই একই ট্রানজ্যাকশনের পরবর্তী কোনো UPDATE-কে রাইট ট্রানজ্যাকশনে পরিণত হতে হয় এবং আপনার রিড শুরু হওয়ার পর অন্য কোনো কানেকশন ডেটা লিখে থাকে, তখন SQLite আপনাকে অপেক্ষা করাতে পারে না। কারণ আপনার স্ন্যাপশটটি ইতিমধ্যে পুরনো হয়ে গেছে এবং অপেক্ষা করলে দুটি কানেকশনের মধ্যে deadlock তৈরি হবে। ডকুমেন্টেশন সরাসরি ফলাফলটি উল্লেখ করে:

পরবর্তী রাইট স্টেটমেন্টগুলো সম্ভব হলে ট্রানজ্যাকশনটিকে রাইট ট্রানজ্যাকশনে আপগ্রেড করবে, অথবা SQLITE_BUSY রিটার্ন করবে।

আপনার 5000 মিলিসেকেন্ডের টাইমআউট এখানে কোনো কাজে আসে না। এররটি তাৎক্ষণিকভাবে চলে আসে, যার ফলে মনে হয় সেটিংসটি কোনো কাজই করেনি।

এর সমাধানটি একটি মাত্র শব্দ।

BEGIN IMMEDIATE;
UPDATE notes SET body = 'edited' WHERE id = 1;
COMMIT;

BEGIN IMMEDIATE কোনো কিছু পড়ার আগেই শুরুতে রাইট লক গ্রহণ করে। এতে কোনো আপগ্রেড করার প্রয়োজন হয় না, তাই এড়ানোর মতো কোনো deadlock থাকে না। ফলে busy handler কার্যকর হয় এবং কানেকশনটি ব্যর্থ না হয়ে নিজের সুযোগের জন্য অপেক্ষা করে। শুধুমাত্র রিড-অনলি ট্রানজ্যাকশনগুলোকে deferred রাখুন। যে ট্রানজ্যাকশনে রাইট অপারেশন আছে, তা অবশ্যই immediate হওয়া উচিত।

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

Litestream দিয়ে নিরবচ্ছিন্ন ব্যাকআপ

প্রতি রাতের কপি এক দিনের রাইট (write) পর্যন্ত ডেটা হারানোর ঝুঁকি তৈরি করে এবং চলমান SQLite ডেটাবেসে cp চালানো এমন কপি তৈরি করতে পারে যা খোলা যায় না। দুটি পদ্ধতি নিরাপদ। sqlite3 app.db ".backup /path/to/backup.db" SQLite-এর অনলাইন ব্যাকআপ ইন্টারফেস ব্যবহার করে এবং চলমান ডেটাবেসে কাজ করে। Litestream আরও এক ধাপ এগিয়ে: এটি WAL ফাইল পর্যবেক্ষণ করে এবং অবজেক্ট স্টোরেজে পরিবর্তনগুলো নিরবচ্ছিন্নভাবে পাঠায়, যা আপনার ডেটা হারানোর ঝুঁকি এক দিন থেকে কমিয়ে প্রায় এক সেকেন্ডে নামিয়ে আনে।

Litestream হলো একটি Go বাইনারি যা আপনার অ্যাপ্লিকেশনের পাশাপাশি চলে। এটি অ্যাপ্লিকেশন এবং ডেটাবেসের মাঝে অবস্থান করে না। আপনার অ্যাপ্লিকেশন আগের মতোই SQLite-এ রাইট করে এবং Litestream WAL ফাইল পড়ে পরিবর্তনগুলো আপলোড করে।

cd /tmp
curl -fsSL -O https://github.com/benbjohnson/litestream/releases/download/v0.5.14/litestream-0.5.14-linux-x86_64.deb
sudo dpkg -i litestream-0.5.14-linux-x86_64.deb
litestream version

v0.5.14 হলো সেই রিলিজ যা জুলাই 2026 অনুযায়ী অফিসিয়াল Linux ইনস্টল পেজে নথিভুক্ত আছে এবং v0.5.15 21 জুলাই 2026 তারিখে প্রকাশিত হয়েছে। রিলিজ পেজে থাকা বর্তমান ট্যাগের সাথে মিল রেখে উভয় লাইনের ভার্সন পরিবর্তন করুন এবং আপনার VPS যদি arm64 হয় তবে সংশ্লিষ্ট arm64 প্যাকেজটি নিন।

কনফিগারেশন ফাইলটি /etc/litestream.yml-এ থাকে। একটি লোকাল ফাইল রেপ্লিকা দিয়ে শুরু করুন, কারণ এটি ক্লাউড ক্রেডেনশিয়াল ছাড়াই পুরো লুপটি পরীক্ষা করতে সাহায্য করে।

dbs:
  - path: /home/appuser/app/app.db
    replica:
      type: file
      path: /var/backups/litestream/app

লক্ষ্য করুন যে ফিল্ডটি হলো replica, একবচন। Litestream 0.5 ভার্সনে 0.3 সিরিজের replicas অ্যারের পরিবর্তে একটি একক রেপ্লিকা ব্লক ব্যবহার করা হয়েছে এবং দুটি এন্ট্রি থাকা কনফিগারেশন এখন স্টার্টআপের সময় ব্যর্থ হয়। অনেক থার্ড-পার্টি গাইডে এখনও পুরনো অ্যারে দেখানো হয়, তাই সার্চে পাওয়া প্রথম উদাহরণের পরিবর্তে উপরের কাঠামোটি কপি করুন। 0.5 সিরিজে litestream wal সাবকমান্ডের নাম পরিবর্তন করে litestream ltx রাখা হয়েছে, কারণ অন-ডিস্ক ব্যাকআপ ফরম্যাট পরিবর্তিত হয়েছে।

কোনো কিছু সক্রিয় করার আগে কনফিগারেশনটি পার্স (parse) হচ্ছে কিনা তা যাচাই করুন।

sudo litestream databases -config /etc/litestream.yml

এরপর হাতে-কলমে রাউন্ড ট্রিপ পরীক্ষা করুন। এই ফরমটি কনফিগারেশন ফাইল এড়িয়ে একটি ডেটাবেসকে একটি পাথে রেপ্লিকেট করে।

mkdir -p /tmp/replica
litestream replicate ~/app/app.db file:///tmp/replica/app

এটি ফোরগ্রাউন্ডে চলে এবং চলতেই থাকে। দ্বিতীয় একটি শেলের মাধ্যমে একটি রো (row) লিখুন এবং রেপ্লিকাটিকে একটি নতুন ফাইলে রিস্টোর করুন।

sqlite3 ~/app/app.db "INSERT INTO notes (body) VALUES ('written after replication started');"
litestream restore -o /tmp/restored.db file:///tmp/replica/app
sqlite3 /tmp/restored.db "SELECT count(*) FROM notes;"

গণনায় নতুন রো-টি অন্তর্ভুক্ত থাকবে। যদি না থাকে, তবে পরিবর্তনটি এখনও সিঙ্ক হয়নি: Litestream প্রতি sync-interval অন্তর ডেটা পুশ করে যার ডিফল্ট সময় 1 সেকেন্ড, তাই অপেক্ষা করুন এবং পুনরায় রিস্টোর করুন। সেই এক সেকেন্ডই হলো আপনার রিকভারি পয়েন্ট। একটি ক্র্যাশের ফলে সর্বোচ্চ শেষ সিঙ্ক ইন্টারভালের রাইটগুলো হারিয়ে যেতে পারে এবং কোনো কনফিগারেশনই এটিকে শূন্য করতে পারে না।

প্রকৃত স্টোরেজের জন্য, রেপ্লিকা ব্লকটিকে একটি S3 URL দিয়ে পরিবর্তন করুন। এটি Amazon S3 এবং অন্যান্য প্রোভাইডারের S3-সামঞ্জস্যপূর্ণ অবজেক্ট স্টোরেজে কাজ করে।

dbs:
  - path: /home/appuser/app/app.db
    replica:
      url: s3://your-bucket-name/app
      region: us-east-1

snapshot:
  interval: 24h
  retention: 24h

ক্রেডেনশিয়ালগুলো সেই ফাইলে রাখবেন না। Litestream এনভায়রনমেন্ট থেকে LITESTREAM_ACCESS_KEY_ID এবং LITESTREAM_SECRET_ACCESS_KEY পড়ে, তাই সেগুলোকে root-এর মালিকানাধীন এবং 600 মোডের একটি systemd ড্রপ-ইন ফাইলে রাখুন।

উপরের স্ন্যাপশট ভ্যালুগুলো ডিফল্ট এবং রিটেনশন ডিফল্ট অনেককে অবাক করে। রিটেনশন হলো Litestream কতদিন স্ন্যাপশট এবং সংশ্লিষ্ট ফাইলগুলো সংরক্ষণ করবে, তাই এটি নির্ধারণ করে আপনি কত সময় আগের ডেটা রিস্টোর করতে পারবেন। চব্বিশ ঘণ্টা মানে হলো, বুধবার সকালে কোনো ভুল মাইগ্রেশন লক্ষ্য করলে সোমবারের অবস্থা থেকে তা আর রিকভার করা সম্ভব নয়। retention: 168h এক সপ্তাহের জন্য সেট করুন এবং অতিরিক্ত স্টোরেজের জন্য খরচ করুন।

আপনার প্রয়োজনে আসার আগেই রিস্টোর প্রক্রিয়াটি যাচাই করুন

litestream restore -o /tmp/check.db /home/appuser/app/app.db
sqlite3 /tmp/check.db "PRAGMA integrity_check;"
sqlite3 /tmp/check.db "SELECT count(*) FROM notes;"

একটি ডাটাবেস পাথ দেওয়া হলে, litestream restore কমান্ডটি /etc/litestream.yml-এ সংশ্লিষ্ট রেপ্লিকাটি খুঁজে বের করে এবং তা ডাউনলোড করে। একটি সঠিক ফাইলের ক্ষেত্রে PRAGMA integrity_check কমান্ডটি ok আউটপুট দেয়; অন্য যেকোনো আউটপুট নির্দেশ করে যে রিস্টোর করা কপিটি ব্যবহারযোগ্য নয়। এটি একটি systemd service and timer-এর মাধ্যমে নিয়মিত বিরতিতে চালান এবং আউটপুটটি পর্যবেক্ষণ করুন। যতক্ষণ না আপনি একবার ব্যাকআপ রিস্টোর করছেন, ততক্ষণ আপনি নিশ্চিত হতে পারবেন না যে এটি কাজ করছে।

systemd-এর অধীনে Litestream চালানো

Debian প্যাকেজটি একটি litestream ইউনিট ইনস্টল করে যা /etc/litestream.yml থেকে পড়ে।

sudo systemctl enable litestream
sudo systemctl start litestream
sudo journalctl -u litestream -f

সফল আউটপুট কনফিগারেশন থেকে প্রতিটি ডাটাবেসের নাম দেখায় এবং পর্যায়ক্রমিক সিঙ্ক লাইন ছাড়া বাকি সময় নীরব থাকে। আপনার ডাটাবেস পাথের বিপরীতে no such file or directory ত্রুটি মানে হলো কনফিগারেশনে পাথটি ভুল, অথবা প্রসেসটি এটি পড়তে পারছে না। ইউনিটটি ডিফল্টভাবে root হিসেবে চলে, যা এই কাজের জন্য প্রয়োজনের চেয়ে বেশি প্রিভিলেজ। Litestream-কে অবশ্যই ডাটাবেস এবং এটি ধারণকারী ডিরেক্টরি উভয়ই পড়তে ও লিখতে সক্ষম হতে হবে, কারণ এটি আপনার ডাটাবেসের পাশে থাকা -wal এবং -shm ফাইলগুলোর সাথে কাজ করে। তাই এটিকে সেই অ্যাকাউন্টটি দিন যা আপনার অ্যাপ্লিকেশন ইতিমধ্যে ব্যবহার করছে।

# /etc/systemd/system/litestream.service.d/override.conf
[Service]
User=appuser
Group=appuser

sudo systemctl daemon-reload এবং sudo systemctl restart litestream দিয়ে এটি প্রয়োগ করুন। সর্বনিম্ন প্রিভিলেজসহ একটি ডেডিকেটেড সার্ভিস অ্যাকাউন্ট সেট আপ করতে কয়েক মিনিট সময় লাগে, এবং এটি একটি ব্যাকআপ এজেন্ট এবং মেশিনে থাকা দ্বিতীয় root প্রসেসের মধ্যে পার্থক্য তৈরি করে।

আপনি যদি কখনো শূন্য থেকে মেশিনটি পুনরায় তৈরি করেন, তবে একটি অর্ডারিং বা ক্রম সংক্রান্ত বিষয় গুরুত্বপূর্ণ। আপনি চাইবেন অ্যাপ্লিকেশন শুরু হওয়ার আগেই ডাটাবেসটি রিস্টোর হোক। litestream restore কমান্ডটি -if-db-not-exists গ্রহণ করে, যা ফাইলটি ইতিমধ্যে সেখানে থাকলে 0 এক্সিট কোড দেয়, তাই প্রতিটি বুটে এটি চালানো নিরাপদ। আপনার অ্যাপ্লিকেশনের ইউনিটে এটি একটি ExecStartPre লাইনে রাখুন; এতে একটি নতুন VPS ডাটাবেসটি নামিয়ে নেবে এবং বিদ্যমান VPS কোনো পরিবর্তন করবে না। আপনি যদি এটি এক জায়গায় রাখতে চান, তবে litestream replicate-এ একটি অনুরূপ -restore-if-db-not-exists ফ্ল্যাগ রয়েছে।

যেখানে VPS-এ SQLite অকার্যকর হয়

নেটওয়ার্ক ফাইলসিস্টেম। এটি এমন একটি সীমাবদ্ধতা যা আপনি কনফিগারেশনের মাধ্যমে এড়িয়ে যেতে পারবেন না। WAL মোড ব্যবহারের জন্য ডাটাবেস ব্যবহারকারী প্রতিটি প্রসেসকে মেমোরির একটি ছোট অংশ শেয়ার করতে হয়, যা -shm ফাইলটি প্রদান করে। SQLite ডকুমেন্টেশনে এই নিয়মটি কোনো শর্ত ছাড়াই স্পষ্টভাবে বলা হয়েছে:

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

তাই মাউন্ট করা NFS (নেটওয়ার্ক ফাইল সিস্টেম) বা SMB শেয়ারে থাকা ডাটাবেস ক্ষতিগ্রস্ত (corrupt) হতে পারে এবং কোনো pragma এটি প্রতিরোধ করতে পারে না। এখানে একটি সূক্ষ্ম পার্থক্য রয়েছে যা অনেকেই এড়িয়ে যান। নেটওয়ার্ক ব্লক ডিভাইস, যা বেশিরভাগ VPS প্রোভাইডার অতিরিক্ত স্টোরেজ হিসেবে যুক্ত করে, তা Linux-এর কাছে একটি সাধারণ ডিস্ক এবং সাধারণ ফাইলসিস্টেম হিসেবেই প্রতীয়মান হয়, এবং এটি ব্যবহার করা নিরাপদ। কিন্তু মাউন্ট করা ফাইল শেয়ার তা নয়।

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

অতিরিক্ত রাইট লোড। এক সময়ে একজন রাইটার (writer) থাকাটা ফাইল ফরম্যাটের একটি বৈশিষ্ট্য, কোনো টিউনযোগ্য সেটিংস নয়। ছোট রাইটগুলো সাশ্রয়ী কারণ প্রতিটি কমিট WAL-এ যুক্ত হয়, তাই থ্রুপুট আপনার CPU-এর চেয়ে ডিস্কের ছোট-রাইট ল্যাটেন্সির ওপর বেশি নির্ভর করে। VPS-এ NVMe বনাম SATA SSD স্টোরেজের পার্থক্য কেমন হয় তা দেখতে এই লিঙ্কে দেখুন। দীর্ঘ ট্রানজ্যাকশনগুলোই আসল সমস্যা, কারণ এগুলো অন্য সকল রাইটারকে সারিবদ্ধভাবে আটকে রাখে।

অ্যানালিটিক্যাল কুয়েরি। SQLite হলো ট্রানজ্যাকশনের জন্য তৈরি একটি রো-স্টোর (row store)। একশ মিলিয়ন রো স্ক্যান করা ড্যাশবোর্ডের কাজ ভিন্ন ধরনের টুলের জন্য, এবং সার্ভার কাজের জন্য DuckDB বনাম SQLite আলোচনায় এই সীমানাটি কোথায় তা তুলে ধরা হয়েছে।

রেপ্লিকেশনের অধীনে VACUUM একটি পূর্ণাঙ্গ VACUUM পুরো ডাটাবেস ফাইলটিকে নতুন করে লেখে, যার অর্থ হলো Litestream-কে পুরো ফাইলটি পুনরায় আপলোড করতে হয়। Litestream ডকুমেন্টেশন রেপ্লিকেশন সক্রিয় থাকা অবস্থায় এটি চালানোর বিরুদ্ধে পরামর্শ দেয়। রেপ্লিকেটর বন্ধ করুন, vacuum করুন, তারপর পুনরায় চালু করুন এবং একটি নতুন পূর্ণাঙ্গ স্ন্যাপশট আশা করুন।

একটি ডাটাবেসে দুটি রেপ্লিকেটর। একই ডাটাবেস বা একই রেপ্লিকা গন্তব্যের বিপরীতে কখনোই দুটি Litestream প্রসেস চালাবেন না। ডকুমেন্টেশনে স্পষ্টভাবে বলা হয়েছে যে এটি প্রতিরোধ করার দায়িত্ব আপনার, এবং এর অন্যথা হলে এমন একটি রেপ্লিকা তৈরি হবে যা আপনি রিস্টোর করতে পারবেন না।

Litestream যা কভার করে না

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

FAQ

SQLite কি প্রোডাকশন অ্যাপ্লিকেশনের জন্য যথেষ্ট ভালো?

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

busy_timeout সেট করার পরেও কেন আমি database is locked পাই?

কারণ SQLite বিজি হ্যান্ডলারকে এড়িয়ে যায় যখন অপেক্ষা করলে ডেডলক হওয়ার সম্ভাবনা থাকে। একটি ট্রানজ্যাকশন যা সাধারণ BEGIN দিয়ে শুরু হয় তা ডিফার্ড (deferred) থাকে: একটি ওপেনিং SELECT এটিকে একটি রিড ট্রানজ্যাকশনে রাখে এবং পরবর্তী রাইট অপারেশনকে আপগ্রেড করতে হয়। যদি এর মধ্যে অন্য কোনো কানেকশন রাইট করে, তবে SQLite আপনার বিজি হ্যান্ডলারকে কল না করেই সরাসরি SQLITE_BUSY রিটার্ন করে, কারণ আপনার রিড স্ন্যাপশটটি ততক্ষণে পুরনো হয়ে যায়। রাইট করার মতো যেকোনো ট্রানজ্যাকশন BEGIN IMMEDIATE দিয়ে শুরু করুন, যাতে রাইট লক শুরুতেই নেওয়া হয় এবং টাইমআউট কার্যকর হয়।

আমি কি আমার SQLite ডেটাবেস নেটওয়ার্ক স্টোরেজে রাখতে পারি?

NFS বা SMB-এর মতো নেটওয়ার্ক ফাইলসিস্টেমে এটি রাখা যাবে না। WAL মোডের জন্য সব প্রসেসের -shm ফাইলের মাধ্যমে মেমোরি শেয়ার করা প্রয়োজন এবং SQLite ডকুমেন্টেশন অনুযায়ী, ডেটাবেস ব্যবহারকারী প্রতিটি প্রসেসকে একই হোস্ট কম্পিউটারে থাকতে হবে। আপনার প্রোভাইডারের দেওয়া নেটওয়ার্ক ব্লক ডিভাইস ভিন্ন বিষয়: Linux এটিকে একটি সাধারণ ডিস্ক এবং ফাইলসিস্টেম হিসেবে দেখে, তাই সেখানে SQLite কাজ করে।

আমি যদি ইতিমধ্যে প্রতিদিন রাতে ব্যাকআপ নিই, তবে কি আমার Litestream প্রয়োজন?

এটি নির্ভর করে আপনি কতটা ডেটা হারানোর ঝুঁকি নিতে পারেন তার ওপর। প্রতিদিন রাতের ব্যাকআপ মানে আপনি চব্বিশ ঘণ্টা পর্যন্ত রাইট করা ডেটা হারাতে পারেন। Litestream প্রতি সেকেন্ডে একবার সিঙ্ক করে, তাই ক্র্যাশ করলে আপনি বড়জোর শেষ এক সেকেন্ডের ডেটা হারাবেন। এটি cp দিয়ে ডেটাবেস ফাইল কপি করার চেয়েও নিরাপদ, কারণ কপি করার সময় ডেটাবেস রাইট মোডে থাকতে পারে। Litestream শুধুমাত্র ডেটাবেস কভার করে, তাই এর পাশাপাশি সাধারণ ফাইল ব্যাকআপ চালু রাখুন।

#sqlite#wal#litestream#backups#production