Files
2nd/10_Wiki/Topic_Programming/Coding/CS_Consistent_Hashing.md
T
Antigravity Agent 9148c358d0 docs(10_Wiki): 위키 전체 재구성 — Topic_* 폴더를 4개 카테고리로 통합 + 대규모 중복 제거
Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를
Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류.

- 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로
  자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거,
  동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거.
- 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming,
  Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business,
  Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로,
  나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는
  title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백).
  원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지.
- 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서.
- 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는
  지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지.
- Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경.
- 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
2026-07-05 00:33:48 +09:00

5.3 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
cs-consistent-hashing Consistent Hashing — Sharding / Cache 분산 Coding draft B conceptual 2026-05-09 2026-05-09
cs
hashing
sharding
vibe-coding
language applicable_to
TS
Backend
consistent hashing
hashring
virtual nodes
jump hash
rendezvous

Consistent Hashing

Hash mod N = node 추가 / 제거 시 거의 모든 key 이동. Consistent hashing = 1/N 만 이동. Memcached / DynamoDB / Cassandra / load balancer 가 사용.

📖 핵심 개념

  • Hash ring: hash 결과를 0..2^32 원으로 배치.
  • Node 도 ring 의 위치.
  • Key 의 hash → 시계방향 다음 node.
  • Virtual nodes: 한 node = N 개 가상 위치 (균형).

💻 코드 패턴

단순 hashring

import { createHash } from 'node:crypto';

class HashRing {
  private ring: Map<number, string> = new Map();
  private sortedKeys: number[] = [];

  constructor(nodes: string[], private vnodes = 256) {
    for (const node of nodes) this.add(node);
  }

  private hash(s: string): number {
    const md5 = createHash('md5').update(s).digest();
    return md5.readUInt32BE(0);
  }

  add(node: string) {
    for (let i = 0; i < this.vnodes; i++) {
      const h = this.hash(`${node}#${i}`);
      this.ring.set(h, node);
    }
    this.sortedKeys = [...this.ring.keys()].sort((a, b) => a - b);
  }

  remove(node: string) {
    for (let i = 0; i < this.vnodes; i++) {
      this.ring.delete(this.hash(`${node}#${i}`));
    }
    this.sortedKeys = [...this.ring.keys()].sort((a, b) => a - b);
  }

  get(key: string): string {
    if (this.sortedKeys.length === 0) throw new Error('empty');
    const h = this.hash(key);
    // Binary search — 시계방향 다음
    let lo = 0, hi = this.sortedKeys.length - 1;
    while (lo < hi) {
      const mid = (lo + hi) >> 1;
      if (this.sortedKeys[mid] < h) lo = mid + 1;
      else hi = mid;
    }
    const idx = this.sortedKeys[lo] >= h ? lo : 0;
    return this.ring.get(this.sortedKeys[idx])!;
  }
}

const ring = new HashRing(['node1', 'node2', 'node3']);
console.log(ring.get('user-42'));  // 항상 같은 node

Add / remove 효과

N nodes → N+1: 약 1/(N+1) key 이동.
mod N: 거의 모든 key 이동.

Replication (replica = ring 의 다음 N 개)

function getReplicas(key: string, n: number): string[] {
  const seen = new Set<string>();
  const result: string[] = [];
  let idx = findIndex(hash(key));
  while (result.length < n && seen.size < this.ring.size) {
    const node = ring.get(sortedKeys[idx % sortedKeys.length]);
    if (!seen.has(node)) {
      seen.add(node);
      result.push(node);
    }
    idx++;
  }
  return result;
}

→ Cassandra / DynamoDB 가 이걸로 N replica 분산.

Jump consistent hash (Google)

// O(log N) 메모리 — 큰 cluster 효율
function jumpHash(key: bigint, numBuckets: number): number {
  let b = -1n, j = 0n;
  let k = key;
  while (j < BigInt(numBuckets)) {
    b = j;
    k = k * 2862933555777941757n + 1n;
    j = (b + 1n) * (1n << 31n) / ((k >> 33n) + 1n);
  }
  return Number(b);
}

→ Buckets 만 — 해시 ring 자체 X. Add 만 가능 (remove 어려움).

Rendezvous (HRW)

function rendezvous(key: string, nodes: string[]): string {
  let max = -Infinity;
  let chosen = '';
  for (const n of nodes) {
    const h = hash(`${n}:${key}`);
    if (h > max) { max = h; chosen = n; }
  }
  return chosen;
}

→ Virtual node 없이 균형. 단 O(N) per lookup.

Maglev (Google, LB)

  • Lookup table 으로 매핑 미리 계산.
  • O(1) lookup, 균등.
  • 큰 LB / packet routing.

사용 예 — distributed cache

// Memcached client
const ring = new HashRing(['cache-1', 'cache-2', 'cache-3']);

async function get(key: string) {
  const node = ring.get(key);
  const client = clients[node];
  return client.get(key);
}

Load balancer

Client request → LB → Backend
LB 가 consistent hashing → 같은 client = 같은 backend (sticky).
HAProxy / nginx ip_hash / Envoy ring_hash.
# Envoy
clusters:
- name: backend
  lb_policy: RING_HASH
  ring_hash_lb_config: { minimum_ring_size: 1024 }

Node weight (heterogeneous)

// 강한 node = 더 많은 vnode
ring.add('big-node', 512);   // 2x vnodes
ring.add('small-node', 256);

🤔 의사결정 기준

상황 추천
Distributed cache Hashring + vnodes
Big cluster (1000s) Jump hash
Replication 필요 Hashring + N successors
LB sticky ring_hash / Maglev
작은 (5 nodes) rendezvous
잦은 add/remove hashring (jump 어려움)

안티패턴

  • Vnodes 없음: 분배 불균등 (50% / 30% / 20%).
  • Bad hash function: 분배 깨짐. md5 / xxhash 같은 균등.
  • Vnodes 너무 많음 (10K+): 메모리 / 시간.
  • Rebalancing 동기 + traffic 끊김: 점진 / read-from-old + write-to-both.
  • Replication factor < 2: node 죽으면 데이터 잃음.
  • Hash collision 무시: 64-bit 이상 사용.
  • Keys distribution 안 검증: skewed traffic.

🤖 LLM 활용 힌트

  • Hashring + vnodes (256-512) = 표준.
  • 큰 cluster = Jump hash.
  • LB = Envoy ring_hash.

🔗 관련 문서