Foundationপ্রথম নীতি থেকে
LEVEL 9লেসন ১৮/১৯অ্যাডভান্সড৫৫ মিনিট

Sharding at Scale — routing, rebalancing, hotspot

Sharding at Scale: Routing, Rebalancing, Hotspots

Partitioning-গণিত শেখা হলেও production-এর আসল খেলা operational: request পৌঁছাবে কোথায়, node যোগ-বিয়োগে কত data নড়বে, আর celebrity-key কোন shard গুলিয়ে দেবে — এই তিন যুদ্ধের map।

এই লেসন শেষে আপনি পারবেন

  • Request-routing-এর তিন topology — client-direct via config-service, proxy tier, DNS sharding — আলাদা করে কোনটা কোন latency-consistency চুক্তি দেয় ব্যাখ্যা করতে পারবেন
  • Metadata chicken-and-egg সমস্যাটা চিনে shard-map-এর মালিককেই কেন নিজের consensus লাগে তা যুক্তি দিয়ে দেখাতে পারবেন
  • Fixed-slot (vnode, pre-split chunk) আর dynamic split/merge rebalancing-এর trade-off হিসাব করে topology-change-এ moved-key % অনুমান করতে পারবেন
  • Celebrity-key hotspot চিনে salting, hot-row splitting আর cache-front — কোন fix কোন workload-এ — বাছাই করতে পারবেন
  • Scatter-gather query-র tail-latency economics আর local-vs-global secondary index-এর ফয়সালা করতে পারবেন

আগে যা বোঝা থাকা দরকার

আগে এটা বুঝি

আগের লেসনে partitioning-sharding শেখেছি placement-গণিত: hash নিন, range ভাগ করুন, key → shard। কাগজে সব পরিষ্কার। Production cluster-এ প্রথম সপ্তাহেই তিনটা প্রশ্ন ঘাড় ধরে ধরে:

  • Request এলো — shard-map-এর কপি তার কাছেই? কে দেখাবে কোন node কোন shard-এর মালিক?
  • Node যোগ-বিয়োগ — data কোথায় কতটা নড়বে, সিদ্ধান্ত কে নেবে?
  • এক user-এর key শার্ডের অর্ধেক QPS খেয়ে ফেলল — আগুন লাগল কোথায়?

এখানেই আগের লেসনদ্বয় ফিরে আসে — gossip-membership (cluster-map ছড়ানোর vehicle) আর raft-consensus (অপ্রত্যাশিত জায়গায়: shard-map-এর মালিক)। আজকের লেসন তিন অধ্যায়ের — routing (request কোথায়), rebalancing (data কতটা নড়ে), hotspot (load কেন অসমান)।

মূল ধারণা

Request routing — তিন topology

Topologyকে জানে shard-mapবাড়তি hopTrade-off
Client-directClient নিজে, config-service থেকে fetchশূন্যClient-library ভারী; stale-map window
Proxy tierProxy-tier, client dumb১ hopTier-ই fleet-wide bottleneck/blast-radius
DNS shardingDNS answer-ই routingশূন্য-ishCoarse — IP-list স্তরেই শেষ

Client-direct হলো Vitess-ধাঁচের দর্শন: client config-service থেকে shard-map snapshot নিয়ে key→shard→node নিজেই resolve করে সোজা database-node-এ যায় — latency সবচেয়ে কম, দাম map-এর stale-window: ভুল node-এ গেলে node-ই hint দেয় ‘metadata refresh করো’। Proxy-tier-এ (vtgate, mongos) client সরল; request proxy-র পেট দিয়ে। DNS-sharding মোটা দানা: service-name-এর পেছনে shard-group-এর IP ঘোরানো — dns lesson-এর TTL-economics-ই এখানে routing-knob, granularity মোটে IP-group।

Metadata-র মুরগি-ডিম

Shard-map নিজেই shared state — সেখানেই পুরনো প্রশ্ন: কে authoritative? Config-service মরলে নতুন client join করবে কীভাবে? Map-এর দুই version দুই component-এ থাকলে কে জেতে?

সৎ উত্তর: shard-metadata-র মালিককে নিজের consensus লাগবেই — raft-quorum চালিয়ে config-store (etcd/consul), নয় gossip-based membership + deterministic derivation। এজন্যই আগের লেসনগুলো পড়া: consensus-এর দাম দিয়ে metadata-কে single-truth রাখা, বা gossip-এর eventual-window মেনে membership-derive করা — দুই-ই সচেতন চয়েস, accident না।

Rebalancing — দুই দর্শন

Node যোগ-বিয়োগ মানে assignment বদলানো। দুই দর্শন:

Fixed-slot, many-slots-per-node। Partition-space আগেই অনেক বড় — consistent-hash ring-এ প্রতি node-এর অনেক virtual node (vnode), বা HBase-ধাঁচে pre-split chunks। Node যোগে slot-ownership হস্তান্তর হয়, slot-সংজ্ঞা বদলায় না। গণিত: n node-এ এক যোগে সরে মাত্র 1/(n+1) ভগ্নাংশ; vnode বেশি হলে skew কমে (law of large numbers)।

Dynamic split/merge। Range-partitioned system load দেখে ভাগ করে: range বড়/ব্যস্ত হলে split, ফাঁকা জোড়া merge — data-driven, surgical; দাম split-এর serving-time copy + threshold ভুলে split-storm।

দিকFixed-slot + vnodesDynamic split/merge
Movement unitSlot-ownership (cheap handoff)পুরো half-range copy
Skew controlVnode-count বাড়িয়ে statisticalLoad-observed, surgical
Ops-complexityLow-mechanismThreshold-tuning-এর দায়

শেষ row-টা মনে রাখবেন: কোনো rebalancing-দর্শনই hot-key সমাধান করে না — key এক থাকলে সে এক shard-এই; hotspot-এর ঔষধ আলাদা।

Celebrity problem — hotspot-এর তিন ঔষধ

celebrity-user একটা post দিল, দশ লাখ follower read করছে — তার key- এর সব lookup এক shard-এ। Hotspot-এর তিন প্রতিষেধক:

  1. Key salting: key-এ random suffix জুড়ে এক logical-key-কে Sটা physical-key-এ ছড়ান — write fan-out S, read scatter-gather S।
  2. Hot-row splitting / replica-fanout: ওই row-র extra replicas — read-heavy হলে কার্যকর।
  3. Cache-front: Redis/CDN ওই key-এর traffic shard-এর আগেই খেয়ে ফেলে — simplest, staleness-window-এর দামে।

Scatter-gather আর secondary-index-এর ফয়সালা

Shard-জুড়ে query (‘সব shard-এ যাও, ফল জোগাড় করো’) মানে p99-এর গণিত উল্টে যায়: একটা shard slow হলেই পুরো query ওই slowest-এর ঘড়িতে। S shard, per-shard slow-probability p হলে overall-slow ≈ 1-(1-p)^S — S=10-এ ১%-slow হলে ≈ 9.6%, S=100-এ ≈ 63%! Shard-count-ই tail-multiplier।

Secondary index-এ ফয়সালা একই গল্পের দুই মুখ: local index (প্রতি shard নিজের doc-এর index) — write-local, read-scatter-gather; global index (attribute-value → আলাদা shard) — read-point, কিন্তু প্রতিটা base-write এখন index-shard-এও write — distributed-write, partial-failure-এ index-drift। Cross-shard transaction-লাগানো পড়লে saga-lesson-এর pattern-ই ওখানকার শেষ কথা।

ভেতরে কী ঘটছে

Consistent hashing — আংটির গণিত

Mod-hash-এর জন্য: n→n+1 node বদলে virtually সব key নতুন owner পায় (h(k) mod n প্রায় সব r-এ বদলায়) — moved ≈ n/(n+1)। Consistent hashing key আর node দুজনকেই এক circle-এ বসায়; key-র owner clockwise-পরের প্রথম node-point:

ring:   n1 ●───● k ───● n3 ───● n2 ───(back to n1)
        key k-র owner = clockwise-পরের point = n3

Node যোগে সে কেবল নিজের arc-এর keys নেয় — moved ≈ 1/(n+1)। Raw-ring-এ random placement luck-এর খেলা (arc ±২০-৩০% skew সহজেই); vnodes সেটাই ঠিক করে — প্রতি node ১০০+ virtual point, arc- দৈর্ঘ্যগুলো central-limit-এ কাছাকাছি। Dynamo paper-এর সবচেয়ে practical উত্তরাংশ এটাই; route হয় sorted-array bisect-এ — O(log V)।

Rebalancing-এর রাস্তার বাস্তবতা — move মানে copy

Ownership-handoff কাগজে instant, বাস্তবে data-copy: নতুন arc-এর keys disk থেকে stream, মাঝপথে dual-write/log-sync, তারপর ownership-switch। জানালাটায় তিন বাস্তবতা:

  • Bandwidth-tax: copy-traffic production-I/O-র সাথে share — throttling knob mandatory।
  • Dual-serving: switch-এর আগে পুরনো-নতুন owner দুজনেই প্রশ্ন পেতে পারে — idempotent-read + version-check-এর প্রস্তুতি।
  • Propagation-lag: gossip/config-poll-এ সব client-এর নতুন map একসাথে আসে না — hint-redirect protocol-টাই সেতু।

Dynamic split-এ যোগ হয় atomicity-প্রশ্ন: copy চলাকালে ওই range-এর write কোথায় জমে? উত্তর system-ভেদে আলাদা (HBase WAL-replay, MongoDB migration-lock) — শিক্ষা এক: movement-ই expensive, minimize-ই design।

উদাহরণ

একটা node-join, শুরু থেকে শেষ — timeline

t0  ৪-node cluster (vnodes=64/node), ৫০০০ keys, ~1250/node
t1  ops: n5 boot — gossip-pool-এ join ঘোষণা (আগের লেসন!)
t2  coordinator/config-service: ring-এ n5-এর ৬৪ point insert
    → প্রতিটা পুরনো node কিছু arc হারাবে; n5 পাবে ~20% ownership
t3  streaming: n1..n4 → n5 data-copy (throttled)
t4  copy-done handshake → ownership-switch, epoch++
t5  shard-map new-version broadcast; clients refresh
t6  straggler: old-map client n3-কে key জিজ্ঞেস করল যেটা এখন n5-এর
    → n3 reply: MOVED-to-n5 + fresh-map-hint

ফলাফল build/experiment-এর সংখ্যার সাথে মিলল: moved ≈ 20–23% (theory 1/(4+1)=20%, vnode-quantization-এ ওঠানামা), কোনো moment-এই data হয়নি।

এবার celebrity: user-viral এক key-এর QPS = বাকি ৪৯৯৯ key-এর মিলিত QPS-এর সমান — rebalancing পারে না, key-এক shard-এক। Salting B=32: user-viral#0..31 ছড়িয়ে ৩২ slot-এ; read-side এখন ৩২ parallel lookup

  • merge — latency জিতেছে variance, হারিয়েছে fan-out cost।

ছবিতে

CLIENT-DIRECT (Vitess-style):
  client ──get-shard-map──► CONFIG-SVC (raft-backed)
     │ ◄──map v42─────────────┘
     └────────key────────────────────► SHARD-n3   (hop=0)

PROXY TIER (mongos/vtgate):
  client ──────────────────────────► PROXY-TIER ──► SHARD-n3
                                       (map ওর কাছে)   (hop=1)

DNS SHARDING (coarse):
  client ──lookup sharding.example.com──► DNS ──► [IP-group-B]
                                     (TTL-এ ঘোরে group-level)
তিন routing-topology — একই query তিন রাস্তায়: client-direct সবচেয়ে কম hop, proxy-tier সবচেয়ে সরল client, DNS সবচেয়ে মোটা দানায়।
BEFORE (4 nodes):          AFTER n5 joins:
n1 ●━━━A━━━●               n1 ●━━━A'━━━●
n2 ●━━━B━━━●               n2 ●━━━B'━━━●
n3 ●━━━C━━━●    ring       n3 ●━━━C'━━━●
n4 ●━━━D━━━●               n4 ●━━━D'┄┄e┄● ← D' হারায় শেষ-খণ্ড e
                           n5 ●━e1━e2━e3━● ← চার node-এর হারানো খণ্ড পায়

moved ≈ 1/(4+1) = 20%      mod-hash-এ একই change: moved ≈ 83%!
Vnode-ring rebalancing — n5 join-এ প্রতিটা পুরনো node নিজের arc-এর শেষ খণ্ডটা হারায়; সরে মাত্র ~1/(n+1); mod-hash-এ উল্টো প্রায় সব key নড়ত।

নিজে চালিয়ে দেখুন

EXPERIMENT

Rebalancing-cost meter + hotspot dashboard — দুই theory-সংখ্যা চোখে

macOS / Linux (Python 3.10+, dependency নেই)· ২০ মিনিট
"""shard_lab.py — rebalancing-cost + hotspot-salting simulation"""
import hashlib
import random
from collections import Counter


def h(s):
    return int(hashlib.md5(str(s).encode()).hexdigest(), 16)


KEYS = [f'key-{i}' for i in range(10_000)]


def mod_map(n):
    return {k: h(k) % n for k in KEYS}


class Ring:
    """consistent-hash ring — প্রতি node-এর vnodesটা virtual point"""

    def __init__(self, nodes, vnodes=100):
        pts = sorted((h(f'{nd}:{v}'), nd)
                     for nd in nodes for v in range(vnodes))
        self.points, self.owners = zip(*pts)

    def owner(self, key):
        pos = h(key)
        lo, hi = 0, len(self.points)          # first point >= pos
        while lo < hi:
            mid = (lo + hi) // 2
            if self.points[mid] < pos:
                lo = mid + 1
            else:
                hi = mid
        return self.owners[lo % len(self.owners)]


def ring_map(n):
    r = Ring([f'n{i}' for i in range(1, n + 1)])
    return {k: r.owner(k) for k in KEYS}


def moved_pct(before, after):
    changed = sum(1 for k in before if before[k] != after[k])
    return 100 * changed / len(before)


print('=== topology-change: কত % key নড়ল (10k keys) ===')
for name, scheme in [('mod-hash   ', mod_map), ('vnode-ring ', ring_map)]:
    grow = moved_pct(scheme(5), scheme(6))
    shrink = moved_pct(scheme(6), scheme(5))
    print(f'{name} add-node 5→6: {grow:5.1f}%   '
          f'remove-node 6→5: {shrink:5.1f}%')
print('theory: mod-hash moves ~n/(n+1), ring moves ~1/(n+1)')

# ── hotspot injection + detector + salting ──
random.seed(7)
counts = Counter()
TOTAL = 100_000
for _ in range(TOTAL):
    if random.random() < 0.40:
        counts['celeb-user'] += 1
    else:
        counts[f'user-{random.randrange(10_000)}'] += 1

def dashboard(c, label):
    mean = TOTAL / len(c)
    top_k, top_n = c.most_common(1)[0]
    print(f'{label}: distinct={len(c)}, top={top_k} '
          f'{100*top_n/TOTAL:.2f}% of QPS = {top_n/mean:.0f}× mean')
    hot = [(k, round(n/mean)) for k, n in c.most_common(3) if n > 100*mean]
    print(f'  flagged (>100× mean): {hot}')

dashboard(counts, 'BEFORE salting')

salted = Counter()
B = 64
for k, n in counts.items():
    if k == 'celeb-user':
        base = n // B
        for b in range(B):
            salted[f'{k}#{b}'] += base
        salted[f'{k}#{0}'] += n - base * B
    else:
        salted[k] += n
dashboard(salted, 'AFTER  salting (B=64)')

নমুনা output:

=== topology-change: কত % key নড়ল (10k keys) ===
mod-hash    add-node 5→6:  83.7%   remove-node 6→5:  83.7%
vnode-ring  add-node 5→6:  14.9%   remove-node 6→5:  14.9%
theory: mod-hash moves ~n/(n+1), ring moves ~1/(n+1)
BEFORE salting: distinct=9971, top=celeb-user 40.05% of QPS = 3993× mean
  flagged (>100× mean): [('celeb-user', 3993)]
AFTER  salting (B=64): distinct=10034, top=celeb-user#0 0.68% of QPS = 68× mean
  flagged (>100× mean): []

তিনটা পাঠ:

  • Mod-hash-এর ৮৩.৭% মানে rebalance-মানেই near-full-copy — production-এ mod-hash নিষিদ্ধ হওয়ার কারণ। Ring-এর ১৪.৯% theory-র ১৬.৭%-এর চারপাশের vnode-quantization-ওঠানামা।
  • Detector-এর threshold (>100× mean)-এ celeb ৩৯৯৩× — রাডারে বজ্র; salting B=64-এ ৬৮× — alert চুপ।
  • Salting-ফাঁদ: distinct-keys ৬৩ বাড়ল, read-side এখন ৬৪-way scatter-gather — spread-এর দাম fan-out।
এটা কী প্রমাণ করে

Topology-change-এ mod-hash প্রায় 84% key সরায় অথচ vnode-ring ~15% — theory-র n/(n+1) বনাম 1/(n+1)-এর হুবহু প্রমাণ; আর 40%-QPS-এর celebrity-key detector-এর রাডারে ধরা পড়ে, salting-এর পরে concentration mean-এর কাছে নেমে আসে।

নিজে বানান

BUILD IT

ShardRouter — vnode ring, movement-report, hot-key guard, salting

Python · ●●●●○
  1. Ring-build — প্রতি node vnodesটা hashed point, sorted array
  2. route(key) — bisect দিয়ে clockwise-owner, O(log V)
  3. movement_report — topology-change-এ moved-keys breakdown
  4. hot_keys — mean-এর factor-গুণ ছাড়ালে flag
  5. salting demo — এক key-কে B-subkey-তে ছড়িয়ে node-বণ্টন
"""shard_router.py — vnode ring + movement report + hot-key guard (~100 line)"""
import bisect
import hashlib
import random
from collections import Counter


def h(s):
    return int(hashlib.md5(str(s).encode()).hexdigest(), 16)


class ShardRouter:
    """consistent-hash ring, প্রতি node vnodesটা virtual point"""

    def __init__(self, nodes, vnodes=64):
        self.vnodes = vnodes
        self.ring = sorted((h(f'{nd}:{v}'), nd)
                           for nd in nodes for v in range(vnodes))
        self.points = [p for p, _ in self.ring]

    def route(self, key):
        i = bisect.bisect(self.points, h(key)) % len(self.ring)
        return self.ring[i][1]

    def map_keys(self, keys):
        return {k: self.route(k) for k in keys}


def movement_report(before, after, label):
    moved = [k for k in before if before[k] != after[k]]
    print(f'\n[{label}] total={len(before)}, moved={len(moved)} '
          f'({100 * len(moved) / len(before):.1f}%)')
    dest = Counter(after[k] for k in moved)
    src = Counter(before[k] for k in moved)
    print('  যারা হারাল :', dict(src.most_common()))
    print('  যারা পেলো  :', dict(dest.most_common()))


def hot_keys(counts, factor=100):
    """mean-এর factor গুণের বেশি QPS নিলে flag"""
    mean = sum(counts.values()) / len(counts)
    return {k: round(n / mean) for k, n in counts.items() if n > factor * mean}


def salt(key, buckets):
    return [f'{key}#{b}' for b in range(buckets)]


# ── 1. routing + topology-change ──
KEYS = [f'user-{i}' for i in range(5_000)]
r4 = ShardRouter(['n1', 'n2', 'n3', 'n4'])
m4 = r4.map_keys(KEYS)
load = Counter(m4.values())
print('4-node load:', dict(sorted(load.items())))

r5 = ShardRouter(['n1', 'n2', 'n3', 'n4', 'n5'])      # n5 join!
m5 = r5.map_keys(KEYS)
movement_report(m4, m5, 'join n5')

r3 = ShardRouter(['n1', 'n3', 'n5'])                  # n2,n4 die
m3 = r3.map_keys(KEYS)
movement_report(m5, m3, 'lose n2+n4')

# ── 2. hot-key detection ──
random.seed(3)
traffic = Counter()
for _ in range(50_000):
    if random.random() < 0.35:
        traffic['celeb-user'] += 1
    else:
        traffic[f'user-{random.randrange(4_000)}'] += 1
print(f'\nhot-keys (>100× mean): {hot_keys(traffic)}')

# ── 3. salting — এক key-কে ছড়িয়ে অনেক key ──
subkeys = salt('celeb-user', 32)
spread = Counter(r5.route(k) for k in subkeys)
print('salted subkeys-এর node-বণ্টন:', dict(sorted(spread.items())))
salted_traffic = Counter()
for k, n in traffic.items():
    if k == 'celeb-user':
        for b, sk in enumerate(subkeys):
            salted_traffic[sk] += max(0, n - b) // len(subkeys)
    else:
        salted_traffic[k] += n
print('after-salt hot-keys:', hot_keys(salted_traffic))

নমুনা output:

4-node load: {'n1': 1014, 'n2': 1354, 'n3': 1379, 'n4': 1253}

[join n5] total=5000, moved=1127 (22.5%)
  যারা হারাল : {'n4': 373, 'n3': 325, 'n2': 243, 'n1': 186}
  যারা পেলো  : {'n5': 1127}

[lose n2+n4] total=5000, moved=1991 (39.8%)
  যারা হারাল : {'n2': 1111, 'n4': 880}
  যারা পেলো  : {'n5': 723, 'n3': 668, 'n1': 600}

hot-keys (>100× mean): {'celeb-user': 1406}
salted subkeys-এর node-বণ্টন: {'n1': 5, 'n2': 7, 'n3': 6, 'n4': 7, 'n5': 7}
after-salt hot-keys: {}

তিনটা পাঠ:

  • Join-এ moved 22.5% — theory 20%, বাড়তিটা vnode-quantization (64/ node); vnodes=512-এ চালালে 20%-এর কাছাকাছি নামব।
  • Initial-load-ই অসমান (1014–1379) — vnode কম হলে statistical luck; production-এর বড় default-vnode-এর কারণ এটাই।
  • Salted subkeys ৫ node-এ ছড়াল — এক key-র আগুন পাঁচ চুলায়; detector নিঃশব্দ। Fan-out-cost read-side-এ — next optimization সেখানেই।

নিজে বাড়ান:

  1. Bounded-load variant — overload-node পেলে next-clockwise-node-এ overflow; max-load-ratio track করুন
  2. Pre-split simulation — fixed 1024-chunk model-এর movement-cost dynamic split-এর সাথে তুলনা করুন
  3. Tail-sim — S shard-এ per-shard latency থেকে aggregate p99; S-বাড়ানো curve-টা আঁকুন

বাস্তব সিস্টেমে

বাস্তব জগতে

Vitess — YouTube-থেকে Slack। MySQL-fleet-এ client-direct + config-service topology-র প্রতিনিধি: vtgate routing, vreplica-stream-এ rebalance — CNCF-project, sharding-control-plane-এর reference design।

Cassandra-র vnodes। Token-allocation gossip-managed; vnode-count tuning (256 → 16) history-তেই vnode-এর own-trade-off: ভালো balance, খারাপ repair-scope।

MongoDB balancer। Dynamic split/merge live নমুনা — chunk-size পার হলে split, imbalance-window-এ migration; threshold-ই ops-knob।

Instagram celebrity-id story। Viral user-id salting + read-side aggregation — hotspot-playbook-এর classic engineering-blog tale।

DynamoDB partition-splitting। 10GB-তে auto-split, burst-এ hot-partition alert + adaptive-capacity — hotspot-এর managed-cloud উত্তর।

যে ভুলগুলো সবাই করে

“Consistent hashing মানে topology-change-এ কোনো key-ই সরে না।”

সরে — শুধু অনেক কম। Add-one-node-এ আদর্শে 1/(n+1), vnode-quantization- এ তার চেয়ে সামান্য বেশি (simulation: 22.5% vs theory 20%)। Promise-টা ‘minimal disruption’, ‘no disruption’ না — copy-bandwidth-ও planning-এ রাখুন।

“Shard যত বাড়াব, throughput তত বাড়বে — ছোট ছোট অনেক shard করি।”

Scatter-gather-এর tail-math উল্টো গল্প বলে: 1-(1-p)^S — shard-count-ই p99-multiplier; metadata-size, connection-overhead — সব O(S)। Shard-size বাছাই balance-vs-fanout optimization, one-direction race না।

“Salting-ই hotspot-এর universal ঔষধ — সব hot-key salt করে ফেলি।”

দাম fan-out: read-side S-way lookup + merge, range-query ভাঙে, unique- constraint জটিল; cold-again key-র un-salt-ও chore। Playbook: detector আগে flag করে, salt শুধু proven-hot key-কে — reactive, reversible plan সহ।

“Global secondary index-ই ভালো — read এক shard-এই পাওয়া যায়।”

Read-side জিতলে write-side হারে: base-write index-shard-এও যায় — partial-failure-এ index-drift, low-cardinality attribute (status=‘active’) নিজেই index-এ hotspot। Local-index-এর scatter-gather-read অনেক workload-এ সস্তা — choice read/write ratio-র।

বুঝেছেন কি না দেখুন

1

Request-routing-এর তিন topology-তে shard-map-এর কপি কার কাছে থাকে, আর বাড়তি hop কয়টা?

স্মরণ

Client-direct: client-এর কাছে (config-service snapshot), hop=০। Proxy-tier: proxy-র কাছে (vtgate/mongos), hop=১। DNS sharding: DNS-answer-এ IP-group স্তরে — granularity মোটা, per-key routing নেই।

2

‘Shard-map একটা shared-state — তাই তার মালিককে consensus লাগবেই।’ যুক্তিটা খুলুন; gossip-only alternative-এর দাম কী?

যুক্তি

একাধিক writer (node-join/leave decision) split-brain-এ দুই conflicting-map ছড়াতে পারে — single-authority দরকার, মানে raft-majority on config-store। Gossip-only: map eventually-converges — দাম propagation-window-এ conflicting-routing (hint-redirect-এ সামলাতে হয়) আর flapping-ঝুঁকি; metadata-plane-এও CAP-খেলা চলে।

3

৮-node vnode-ring-এ এক node যোগে আদর্শ moved %? Mod-hash-এ? ১০০০ shard scatter-gather-এ per-shard slow-prob ১% হলে overall-slow?

প্রয়োগ

Ring: 1/(8+1) ≈ 11.1%। Mod-hash: 8/9 ≈ 88.9% — near-total reshuffle। Tail: 1 − 0.99¹⁰⁰⁰ ≈ 99.96% — প্রায় প্রতিটা query একটা slow-shard- এর ঘড়িতে; ১০০০-shard scatter-gather OLTP-তে non-starter।

4

Dynamic split hot-range-কে দুভাগ করলেও hot-key-এর সমস্যা কেন থেকে যায়? কোন অবস্থায় split তবু সাহায্য করে?

যুক্তি

Split ভাগ করে range, key নয় — এক key-র request তবু এক shard-এ, আগুন অটুট। Split সাহায্য করে heat যখন many-distinct-keys-এর range- জড়ো-হওয়া থেকে — ভাগে, দুই node ভাগ করে নেয়। Hot-single-key-এর ঔষধ ভিন্ন: salting, replica-fanout, cache-front।

5

Social-feed table: user-post point-read dominant, analytics-team মাঝে মাঝে all-posts-by-hashtag scan চায়। Local না global secondary index? Hashtag-এর cardinality কোথায় ভূমিকা রাখে?

ডিজাইন

Point-read-dominant OLTP → write-path সস্তা রাখুন: local-index + rare-scan dedicated analytics-path-এ (batch scatter-gather, আলাদা SLA)। Global-index তখনই যখন hashtag-lookup real-time-interactive। High- cardinality tag global-index-এ সুন্দরভাবে ছড়ায়; low-cardinality (#trending) index-shard-কেই hotspot করে — তখন salt/split-ও লাগে।

এরপর কী

এরপর কী

Routing, rebalancing, hotspot — shard-cluster-এর যন্ত্র এখন হাতে। কিন্তু p99 উঠলে প্রশ্ন হবে: কোন request, কোন service-সীমানায়, কোন span-এ সময় গিলল? Logs বলে discrete-events, metrics বলে aggregates — এক request-এর causal-journey-র মানচিত্র দেয় শুধু distributed tracing। পরের লেসনে trace-span anatomy, W3C traceparent-এর byte-dissection, head/tail sampling-এর retention-economics, আর নিজের হাতে একটা TraceCollector — waterfall, critical-path, orphan-diagnostics সহ। Module-এর শেষ লেসন — দেখা হবে wrap-up-এ।

আরও পড়ুন

  • Designing Data-Intensive Applications — Partitioning ও Consistency chapters — Martin Kleppmann · Partitioning strategies, rebalancing-এর তিন প্রকার আর request-routing-এর classification — এই লেসনের কাঠামোর ভিত্তি
  • Dynamo: Amazon's Highly Available Key-value Store (SOSP 2007) — Giuseppe DeCandia, Deniz Hastorun, Madan Jampani, et al. · Consistent-hashing ring-এ virtual node-এর জন্ম — vnode-rebalancing-এর মূল engineering reference