Replication — streaming আর logical-এর দুই পথ
Replication: Streaming and Logical
WAL stream অন্য machine-এ গেলেই replica — কিন্তু কাঁচা byte না decoded row? Streaming আর logical পথের দুই জগৎ, lag-এর গণিত, আর failover-এর রক্তক্ষয়ী সত্যগুলো।
আগে এটা বুঝি
এখন পর্যন্ত আমাদের database একটা মাত্র machine — crash হলে recovery ফিরিয়ে আনে, কিন্তু machine-টাই না বেঁচে থাকলে? Disk পুড়ে গেলে? আর ঢাকার user যদি Virginia-র datacenter-এ ৩০০ms পিং-এ টানে?
সমাধানের নাম replication: একই data ক’টা machine-এ রাখা। আর আমাদের ভাগ্য ভালো — আগের দুই লেসনে আমরা ইতিমধ্যে সেই যন্ত্রটা তৈরি করে ফেলেছি যেটা replication চায়। WAL কী? Data-র প্রতিটা পরিবর্তনের পূর্ণ বিবরণ, কঠোর ক্রমে। সেই log টাই যদি অন্য machine-এ পাঠিয়ে সেখানেও replay করা যায়? দ্বিতীয় machine হুবহু একই অবস্থায় পৌঁছাবে।
কিন্তু দুইটা আলাদা পথ আছে: streaming — কাঁচা WAL byte শিপিং, হুবহু একই cluster-copy গড়া; আর logical — log থেকে row-level event decode করে ভিন্ন structure-এও প্রয়োগ করা। আজকে দুই পথই হাঁটব, lag-এর গণিত মাপব, আর failover-এর জঘন্য বাস্তবতা — split-brain — দেখব।
মূল ধারণা
কেন replicate — তিনটা আলাদা উদ্দেশ্য
| উদ্দেশ্য | যন্ত্র | খরচ |
|---|---|---|
| High availability | primary মরলে standby promote | বাড়তি hardware + failover-complexity |
| Read scaling | read-query standby-এ ছড়ানো | lag-এর stale-read সমস্যা |
| Geo-latency | region-local read replica | দূরত্ব = বড় lag |
লক্ষণীয়: write-scaling এই তালিকায় নেই — single-leader design-এ সব write leader-এই যায়। Write-scale-এর পথ শarding (দুই লেসন পরে), replication নয়।
Physical streaming — WAL byte সমুদ্রযাত্রা
Primary-র TCP connection-এ walsender process WAL-এর কাঁচা byte stream করে; standby-র startup process সেটা receive করে local WAL-এ লেখে, তারপর apply করে — একই block-layout, একই page, হুবহু clone। এজন্যই একে বলে physical: byte-for-byte অভিন্ন cluster।
Standby যদি hot_standby=on থাকে, apply চলতে চলতেই সেখানে
read-only query চালানো যায় — এটাই hot standby। Query-র
snapshot standby-র apply-progress অনুসরণ করে, তাই primary-র
long transaction থাকলে standby-তে query-cancel-ও ঘটতে পারে
(replay-কে অপেক্ষা করতে হয়) — এই সূক্ষ্মতা production-এর
পরিচিত ব্যথা।
Lag — চারটা LSN দিয়ে পড়া
pg_stat_replication view-তে চারটা position জমা হয়, আর এদের
ব্যবধানেই lag-এর পুরো গল্প:
sent_lsn ──► write_lsn ──► flush_lsn ──► replay_lsn
primary standby-র standby-র standby-র
পাঠিয়েছে OS buffer-এ disk-এ fsync apply করেছেsent_lsn − replay_lsn= total lag, standby-র চোখ কতটা পিছিয়েwrite_lag / flush_lag / replay_lag= ধাপ-ভেদে সময় — network slow, না standby-disk slow, না apply slow, আলাদা করে বলে দেয়
synchronous_commit — কোন মুহূর্তে উত্তর দেব
Commit-ack কোন বাধার পরে দেব — এই switch-টাই durability-র দাম-তালিকা:
| Level | Ack দেওয়া হয় যখন | Crash/failover-এ হারায় |
|---|---|---|
off | local WAL buffer-এই | সদ্য-commit হারাতে পারে (corruption নয়) |
local | primary-র local disk fsync | primary মরলে টিকে, কিন্তু standby-তে পৌঁছায়নি হলে হারায় |
remote_write | standby-র OS-এ পৌঁছেছে (disk নয়) | standby OS-crash-এ হারাতে পারে |
on (remote_flush) | standby-র disk fsync | প্রায় কিছুই না — কিন্তু apply-র আগে হলে read পুরনো |
remote_apply | standby apply করেছে | কিছুই না — read-after-write পর্যন্ত গ্যারান্টি |
দাম ঊর্ধ্বমুখী: off → sub-ms, remote_apply → full network
round-trip + apply। Default on।
ভেতরে কী ঘটছে
Logical replication — byte নয়, অর্থ
Streaming-এর সীমা: destination হুবহু একই version-এর একই layout হতে হয়। কিন্তু যদি চাই — শুধু ক’টা table, PG-14 থেকে PG-17-তে upgrade-এর মাঝে zero-downtime migration, বা অন্য কোনো system- এ feed? তখন দরকার log-এর অর্থ: row-level INSERT/UPDATE/ DELETE event-এ decode করা।
যন্ত্রটা:
- Publication (primary-তে): কোন tables publish হবে
- Subscription (subscriber-এ): কোন publication consume হবে
- Replication slot (primary-তে): consumer কোথায় আছে তার bookmark — এবং এর ছায়া-দায়িত্ব: bookmark-এর পেছনের WAL মুছে ফেলা যায় না
Decode-কৃত event subscriber-এ logical worker প্রয়োগ করে — destination-এর schema ভিন্ন হতে পারে (column বাড়তি থাকতে পারে), version ভিন্ন হতে পারে। Logical replication DDL বহন করে না — table দুই পাশে ম্যানুয়ালি মেলাতে হয়; আর দুই পাশেই write চালু করলে conflict — কোনো auto-resolution নেই, মানুষ বসে ঠিক করবে। এই সীমাগুলো feature নয়, দর্শন: logical layer সরল রাখা।
Failover — RPO, RTO আর split-brain
Failover = primary মরে গেলে standby-কে নতুন primary বানানো। দুইটা সংখ্যায় এর গুণমান মাপা হয়:
- RPO (Recovery Point Objective): কতটা data হারাবেন সর্বোচ্চ —
async replication-এ lag-এর সমান;
remote_apply-এ প্রায় শূন্য - RTO (Recovery Time Objective): service down কতক্ষণ — detection + promotion time
আর সবচেয়ে বিপজ্জনক ব্যর্থতা: split-brain। Network ভাঙল
(primary আসলে জীবিত, কিন্তু বাকিরা ভাবে মরা) — দুই node-ই নিজেকে
primary ভেবে write নিতে শুরু করল। দুই শাখায় ভিন্ন history —
merge-এর উপায় নেই। প্রতিরোধের নাম fencing: পুরনো primary-
কে প্রমাণিতভাবে নিষ্ক্রিয় করা (STONITH — “shoot the other node
in the head”: power-cut, firewall-block, বা সরাসরি
pg_ctl stop -m immediate)। নিয়ম কঠোর: promote-এর আগে
fence — ব্যতিক্রম নেই।
Consensus-based HA tool-রা — Patroni, repmgr, Stolon — এই দুর্বল মানুষ-চালিত ধাপগুলো automate করে: etcd/ZooKeeper-এর leader-election দিয়ে একমাত্র-প্রমাণযোগ্য primary, সাথে fencing built-in। ভেতরের consensus গণিত distributed-systems module-এর বিষয় — এখানে নাম-পরিচয়টুকুই যথেষ্ট।
উদাহরণ
একটা দিনের lag-এর ডায়েরি
১০:০০ স্বাভাবিক রান্নাবাজার
lag ≈ 64KB ≈ 40ms — অদৃশ্য, অসংখ্যানুভূত
১০:১৫ বিশাল COPY: ২ GB import শুরু
primary এক নিঃশ্বাসে WAL ২ GB; network 1Gbps — ≥১৬ সে
lag spike: replay 0/50A00000 বনাম sent 0/CE000000
standby-র read-query-রা এখন ১৬+ সেকেন্ড পুরনো snapshot!
১০:২২ import শেষ -> burst-apply, ৯০ সেকেন্ডে catch-up
১১:৪৫ primary-র disk controller মরল (remote_write mode)
১১:৪৫:০৭ detection | ১১:৪৫:২২ fence + promote
RPO: ০-৬ সেকেন্ডের commit | RTO: ২২ সেকেন্ডলক্ষ্য করুন:
- Lag spike primary-র write-rate-এর ফাংশন — bulk-import মানেই lag-এর ঋতু
- Catch-up burst-এ standby apply দ্রুত, কিন্তু CPU/I/O তখন standby-র read-workload-ও ধীর করে
- RPO আর RTO আলাদা axis — sync level RPO, automation RTO ঠিক করে
ছবিতে
দুই পথের মানচিত্র
PRIMARY WIRE STANDBY
┌──────────────┐ ┌──────────────┐ ┌──────────────────┐
│ txn COMMIT │ │ WAL bytes │ │ walreceiver │
│ WAL append │──walsender──►(TCP)──────►│ │ write→OS buf │
│ ▲ │ │ │ │ flush→fsync │
│ sent_lsn │ │ │ │ replay→apply │
└──────────────┘ └──────────────┘ │ ▲ │
│ flush_lsn replay │
write_lsn ──────────────┘ │ │
(OS-buffer mark) └──────────────────┘
lag = sent_lsn − replay_lsn
sync ack levels ঠিক করে: কোন মুহূর্তে client উত্তর পাবে —
off / local / remote_write / remote_flush / remote_applyPRIMARY SUBSCRIBER
┌──────────────┐ ┌───────────────┐ ┌──────────────────┐
│ WAL │ │ logical │ │ logical worker │
│ Heap INSERT │───►│ decoding │──►│ INSERT row │
│ txid 742 │ │ (slot: pos) │ │ UPDATE row │
│ Heap UPDATE │ └───────────────┘ │ DELETE row │
└──────┬───────┘ └──────────────────┘
│ publication: কোন tables
│ subscription: কোন publication
▼
slot retains WAL ←── consumer stalled হলে এখানেই জমে disk-fillনিজে চালিয়ে দেখুন
Standby দাঁড় করান, lag পড়ুন, sync-এর দাম মাপুন
ধাপ ১ — দুই-node lab (native, docker-ও চলে):
initdb -D /tmp/pgp -A trust -U admin
initdb -D /tmp/pgs -A trust -U admin
printf "port=6001\nwal_level=replica\nmax_wal_senders=5\n" >> /tmp/pgp/postgresql.conf
echo "host replication repl 127.0.0.1/32 trust" >> /tmp/pgp/pg_hba.conf
pg_ctl -D /tmp/pgp -l /tmp/pgp.log start
psql -p 6001 -c "CREATE ROLE repl WITH REPLICATION LOGIN PASSWORD 'r';"
pg_basebackup -h 127.0.0.1 -p 6001 -U repl -D /tmp/pgs -R -X stream
printf "port=6002\nhot_standby=on\n" >> /tmp/pgs/postgresql.conf
pg_ctl -D /tmp/pgs -l /tmp/pgs.log start(-R standby.signal + primary_conninfo লিখে দেয় — standby
নিজেই জুড়ে নেবে।)
psql -p 6002 -c "SELECT pg_is_in_recovery();" # t — এটা standbyধাপ ২ — lag-এর চার মুখ:
-- primary-তে:
CREATE TABLE beat(n int);
INSERT INTO beat VALUES (generate_series(1,1000));
SELECT application_name, state,
sent_lsn, write_lsn, flush_lsn, replay_lsn,
write_lag, flush_lag, replay_lag
FROM pg_stat_replication;নমুনা (annotated):
application_name | walreceiver
state | streaming
sent_lsn | 0/50123456 <- primary পাঠিয়েছে
flush_lsn | 0/50123456 <- standby disk-এ — প্রায় ধরা
replay_lsn | 0/50121000 <- apply সামান্য পিছিয়ে
replay_lag | 00:00:00.08 <- মোট ~৮০msএবার pressure-test:
INSERT INTO beat SELECT generate_series(1, 3000000);
SELECT pg_wal_lsn_diff(sent_lsn, replay_lsn) AS lag_bytes,
replay_lag FROM pg_stat_replication;Import চলাকালীন বারবার চালান — lag বেড়ে-কমে দেখুন (example- section-এর ডায়েরিটা চোখে)।
ধাপ ৩ — slot-এর দাঁড়িপাথর:
SELECT slot_name, active, restart_lsn,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(),
restart_lsn)) AS retained
FROM pg_replication_slots;এবার standby ঘুমান করুন: pg_ctl -D /tmp/pgs stop, তারপর
primary-তে কিছু write করে আবার এই query — retained বেড়েছে।
Standby ফিরিয়ে আনুন — retained কমে শূন্য-দিকে। এই retention-ই
stalled-consumer-এর অভিশাপ; বাস্তবে max_slot_wal_keep_size-ই
সেতু-ভাঙা safety।
ধাপ ৪ — sync-level-এর দাম:
\timing on
CREATE TABLE bench(g int);
SET synchronous_commit = off;
INSERT INTO bench SELECT g FROM generate_series(1,500) g;
SET synchronous_commit = local;
INSERT INTO bench SELECT g FROM generate_series(1,500) g;
SET synchronous_commit = remote_apply; -- standby apply পর্যন্ত অপেক্ষা
INSERT INTO bench SELECT g FROM generate_series(1,500) g;
DROP TABLE bench;off প্রায়-মুহূর্তের, local fsync-ভারী, remote_apply প্রতি
statement-এ network round-trip + apply। Batch-size বাড়ালে per-row
amortize — production-এ batch-write-এর অন্যতম কারণ।
pg_ctl -D /tmp/pgs stop && pg_ctl -D /tmp/pgp stop && rm -rf /tmp/pgp /tmp/pgsLag কোনো abstract সংখ্যা নয় — চার LSN-field-এ জীবন্ত; slot-এর WAL-retention চোখে দেখা যায়; আর sync-level flip করলে commit-latency হাতে-কলমে বদলে যায়।
নিজে বানান
LogShipper — lag curve, partition, আর catch-up burst
- Primary প্রতি tick-এ কয়েকটা frame emit করুন — numbered WAL
- Network delay model — frame arrival_tick = emit_tick + jitter
- Partition injection — সময়সীমায় frames retain হোক, না হারাক
- Standby capped-rate-এ apply করুন — lag curve স্যাম্পল করুন
- Heal-এর পরে catch-up burst মাপুন — divergence window হিসাব
লক্ষ্য: replication-lag-এর পুরো জীবনচক্র simulation — স্বাভাবিক ঢেউ, partition-এর প্রশস্ত উপত্যকা, heal-এর পরে খাড়া পুনরুদ্ধার।
import random
APPLY_RATE = 8 # standby প্রতি tick-এ সর্বোচ্চ এত frame apply
def simulate(delay_max=2, partition=(40, 71), horizon=140, seed=11):
rng = random.Random(seed)
lsn = applied = 0
wire = [] # (arrive_tick, frame)
held = [] # partition-এ আটকে থাকা frames
lag = {}
phase = {}
healed_at = None
caught_up_tick = None
for t in range(1, horizon + 1):
in_partition = partition[0] <= t <= partition[1]
# --- primary: ~3 frames/tick emit ---
for _ in range(rng.randint(2, 4)):
lsn += 1
if in_partition:
held.append(lsn) # WAL retained — হারায় না
else:
wire.append((t + rng.randint(1, delay_max), lsn))
# --- heal: retained frames এখন পথে ---
if healed_at is None and t == partition[1] + 1:
healed_at = t
for f in held:
wire.append((t + rng.randint(1, delay_max), f))
held.clear()
# --- standby: পৌঁছানো frames, capped-rate apply ---
arrived = sorted(fr for at, fr in wire if at <= t)
wire = [(at, fr) for at, fr in wire if at > t]
for fr in arrived[:APPLY_RATE]:
applied = max(applied, fr)
# cap ছাড়ানো frames হারায় না — পরের tick-এর queue-তে ফেরে
wire.extend((t + 1, fr) for fr in arrived[APPLY_RATE:])
lag[t] = lsn - applied
if healed_at and caught_up_tick is None and lag[t] == 0:
caught_up_tick = t
phase[t] = ("PARTITIONED" if in_partition else
"CATCHING UP" if healed_at and lag[t] else "normal")
return lag, phase, healed_at, caught_up_tick
lag, phase, healed, done = simulate()
print("tick lag অবস্থা")
for t in range(10, len(lag) + 1, 10):
print(f"{t:>4} {lag[t]:>4} {phase[t]}")
worst_t = max(lag, key=lag.get)
divergence = sum(1 for v in lag.values() if v > 0)
print(f"\nmax lag : {max(lag.values())} frames "
f"(tick {worst_t})")
print(f"divergence window : {divergence} ticks")
print(f"heal -> caught up : tick {healed} -> {done} "
f"({done - healed} ticks)")প্রত্যাশিত output-এর আকার:
tick lag অবস্থা
10 2 normal
20 3 normal
30 2 normal
40 14 PARTITIONED
50 74 PARTITIONED
60 134 PARTITIONED
70 194 PARTITIONED
80 120 CATCHING UP
90 40 CATCHING UP
100 0 normal
...
max lag : 196 frames (tick 71)
divergence window : 62 ticks
heal -> caught up : tick 71 -> 97 (26 ticks)তিনটা পাঠ:
- Partition-এ lag প্রায় linearly বাড়ে — write-rate × সময়, এটাই async replication-এর RPO-র গাণিতিক ভিত্তি
- Heal-এর পরে সোজা শূন্যে না — APPLY_RATE-cap-এর কারণে catch-up-ও সময় নেয়; বাস্তব standby-র replay I/O-ও এমনি capped
heldframes হারায়নি — WAL-retention-এর মডেল; কিন্তু simulation-এ disk-unlimited, বাস্তবে সেটাই slot-এর বিল
নিজে বাড়ান:
held-এর জন্য disk-budget যোগ করুন — overflow হলে frames drop (slot-invalid) — আর দেখুন divergence permanent হয়ে যায়- Sync-mode অনুকরণ — client-ack শুধু তখনই যখন
applied >= lsn; partition-এ availability কী হয়, গুনুন (CAP-এর প্রিভিউ!) - Apply-rate-কে lag-dependent বানান — বড় backlog মানে তুলনায় দ্রুত drain (বাস্তব restore_command/walreceiver-এর আচরণ)
বাস্তব সিস্টেমে
বাস্তব জগতে replication
Cloud-managed HA-র ভিত্তি। RDS Multi-AZ, Cloud SQL HA, Azure Database flexible-server — সবগুলোর ভেতরেই এই লেসনের যন্ত্র: WAL-ship + automatic failover + fence। আপনি button টেপেন, ভেতরে walsender চলে।
Read-replica fleet। Analytics-heavy product-গুলোতে BI-query
standby-pool-এ, OLTP primary-তে — lag-sensitive report-এর জন্য
remote_apply session-level, বাকি সব async — এই mixed-pattern
সাধারণ practice।
Zero-downtime major-version upgrade। Logical replication- ই আজ PostgreSQL-এর official পথ: নতুন-version cluster পাশে দাঁড় করানো, publication-subscription-এ initial sync + ongoing catch-up, তারপর এক মুহূর্তে app-cutover — পুরনো যুগের dump-restore downtime আর নেই।
Change-data-capture শিল্প। Debezium-জাতীয় CDC platform logical decoding-কেই Kafka-topic-এ রূপান্তর করে — cache-invalidate, search-index (Elasticsearch), warehouse-feed সব WAL-এর উপজাত। “Log is the truth”-দর্শনের শিল্প-রূপ।
Split-brain-এর বাস্তব কবরস্থান। নিজের-হাতে failover-script লেখা team-দের ক্লাসিক দুর্ঘটনা: network-flap-এ দুই primary, দুই শাখার data — merge অসম্ভব, ম্যানুয়াল triage-এ ঘণ্টা। এজন্যই Patroni-জাতীয় consensus-tool-ই de-facto standard — fencing-কে amateur-এর হাত থেকে বাঁচানো।
যে ভুলগুলো সবাই করে
“Replication backup-এর বিকল্প।”
না — replication logical দুর্ঘটনাও হুবহু copy করে: ভুলে
DELETE FROM orders (WHERE clause ভুলে!) চালালে সেটা সব replica-
তেও faithful-ভাবে পৌঁছাবে। Backup-এর point-in-time restore সেই
ভুল থেকে ফিরিয়ে আনে; replication availability দেয়, ভুল-সুরক্ষা
নয়। দুটো আলাদা insurance, দুটোই লাগে।
“Async replication-এ committed data কখনো হারায় না।”
Committed মানে local-WAL durable — primary-র disk-এ। Async-এ
standby-তে পৌঁছানো এখনো বাকি থাকতে পারে; primary-র disk পুড়ে
গেলে সেই সদ্য-committed transactions হারায় — ঠিক lag-bytes
ততটুকু। এই জানালাই synchronous_commit=remote_write/on-এর
কেনার জিনিস।
“Hot standby-তে query "প্রায় real-time" — সব report ওখানেই ফেলে দিই।”
Lag-spike-এর সময় standby-র snapshot সেকেন্ড-মিনিট পুরনো —
inventory-check বা balance-report যদি exactness চায়, ভুল উত্তর।
আর long standby-query replay-কে আটকালে query-cancel-ও খাবে
(max_standby_streaming_delay-এর বিচার)। Stale-tolerance
workload-ভেদে decide হয়, default-এ নয়।
“Split-brain মানে দুটো primary — সমস্যাটা তখনই ধরা পড়বে, সহজে fix হবে।”
সবচেয়ে ভয়ের কথা: split-brain নীরব হতে পারে — দুই শাখাই traffic serve করছে, error কোথাও নেই, যতক্ষণ না দুই শাখার ভিন্ন-history data মেলাতে যান। Detection hard, merge impossible, fix মানে এক শাখার লেখা ম্যানুয়ালি বাছাই-করে হারানো। এজন্যই preventive fencing-ই একমাত্র সঠিক strategy।
বুঝেছেন কি না দেখুন
1pg_stat_replication-এর চারটা LSN field আর তাদের ক্রম — এক
line-এ।
স্মরণ
pg_stat_replication-এর চারটা LSN field আর তাদের ক্রম — এক
line-এ।sent_lsn (primary পাঠিয়েছে) → write_lsn (standby OS-buffer) →
flush_lsn (standby disk fsync) → replay_lsn (apply শেষ)।
Total lag = sent − replay; মাঝের ব্যবধানগুলো বলে দেয় বোতলনেক
network-এ, standby-disk-এ, না apply-তে।
2remote_write আর remote_flush (on)-এর মধ্যে ঠিক কী পার্থক্য?
কোন দুর্যোগে প্রথমটা data হারায় কিন্তু দ্বিতীয়টা হারায় না?
যুক্তি
remote_write আর remote_flush (on)-এর মধ্যে ঠিক কী পার্থক্য?
কোন দুর্যোগে প্রথমটা data হারায় কিন্তু দ্বিতীয়টা হারায় না?remote_write: standby-র OS page-cache-এ পৌঁছালেই ack —
disk ছোঁয়নি। remote_flush: fsync হয়েছে। দুর্যোগ: standby-
এর machine-এ power-loss/kernel-panic — page-cache volatile,
fsync’d data নয়। তাই remote_write নেটওয়ার্ক-বিপর্যয় থেকে
বাঁচায়, host-crash থেকে নয়।
3Write-rate গড়ে 2 MB/s, peak-এ 20 MB/s। Async standby-র network
10 MB/s। Peak ৩০ সেকেন্ড চললে সর্বোচ্চ lag কত? এই সিস্টেমের
theoretical best-RPO কত?
প্রয়োগ
Deficit: 20−10 = 10 MB/s × ৩০ সে = ৩০০ MB backlog —
peak-শেষে lag। তারপর 10 MB/s surplus-rate-এ (গড় write 2 মানে
drain-rate 8 MB/s) প্রায় ৩৮ সেকেন্ডে catch-up। Best-RPO =
peak-শেষ মুহূর্তে primary মরলে হারানো ডেটা = ~৩০০ MB-র WAL —
এটাই async-এর দাম।
4Stalled consumer-এর slot কেন primary-কে মেরে ফেলে — যেখানে
dead standby-র streaming connection তেমন ক্ষতি করে না?
যুক্তি
দুটোই slot ব্যবহার করে — পার্থক্য visibility-তে: dead connection monitoring-এ চোখে পড়ে (state != streaming), alert হয়। কিন্তু মূল ক্ষতি দুটোতেই এক: slot restart_lsn-এর পেছনের WAL retention — consumer এগোবে না, checkpoint WAL মুছতে পারবে না, pg_wal ফুলবে। Silent-stall (connection alive, apply stuck) তাই worst — চোখে দেখা যায় না, বিলটা তবু জমে। Monitoring: retained-bytes alert।
5Team-এর failover runbook-এ লেখা: “primary unresponsive 60s দেখলে
standby promote করো।” এই rule-এ split-brain-এর ফাঁক কোথায়?
Fence-step যোগ করলে ঠিক কী করতে হবে?
ডিজাইন
ফাঁক: primary আসলে alive কিন্তু network-isolated (monitoring-দের
কাছে unresponsive) — promote-এর পরে দুই primary, দুই শাখা
write। Fence-step: promote-এর আগে পুরনো primary-কে প্রমাণিত
নিষ্ক্রিয় করা — reachable হলে pg_ctl stop -m fast/immediate,
না-reachable হলে infrastructure-level (VM power-off, firewall
drop 5432)। আর promote-করা node নিজেও নিশ্চিত হবে পুরনো primary-
এর সাথে কোনো live connection নেই। Rule-এর নতুন রূপ: detect →
fence → verify-dead → promote।
এরপর কী
এরপর কী
Replication এক data-কে ক’টা machine-এ নকল করে — কিন্তু একটা table-ই যদি কোটি row-এর, এক machine-এর memory/disk/index-ও যদি যথেষ্ট না হয়? তখন data-কে ভাঙতে হয়। পরের লেসনে: single- machine partitioning (RANGE/LIST/HASH, pruning), তারপর আসল sharding — consistent hashing ring-এর গণিত, যেখানে দেখব কীভাবে node যোগ করলে মাত্র 1/n ভগ্নাংশ key সরে।
আরও পড়ুন
- PostgreSQL Documentation — High Availability, Load Balancing, and Replication — PostgreSQL Global Development Group · Streaming/logical replication, sync levels, slots — লেসনের PostgreSQL-specific উৎস
- Designing Data-Intensive Applications, Chapter 5 — Replication — Martin Kleppmann · Leader-follower, replication lag, multi-leader-এর প্রামাণ্য ওভারভিউ
- Database System Concepts — Distributed Databases chapters — A. Silberschatz, H. Korth, S. Sudarshan · Replication আর consistency-র পাঠ্যপুস্তক ভিত্তি