← 문제로

48. 카피-온-라이트(COW) 라우팅 테이블의 동시 갱신 (C#)

난이도 최상
내 리뷰 · C#
해설 · C#

해설 — 카피-온-라이트(COW) 라우팅 테이블의 동시 갱신 (C#)

난이도: 최상

요약

SetRoute 는 현재 스냅샷을 읽고(read) 수정본을 만든 뒤(copy-modify) 무조건 대입한다(store). 이 read-modify-write 가 여러 스레드에서 원자적으로 직렬화되지 않아, 두 갱신이 같은 current 를 읽고 각자의 수정본으로 덮어쓰면 하나의 갱신이 유실된다(lost update). "원자적 포인터 교체"는 읽기를 안전하게 할 뿐, 갱신들 사이의 원자성을 주지 않는다.

문제점

  • (A) CAS 없는 무조건 교체 → 갱신 유실: 스레드 T1 이 SetRoute(1, "a"), T2 가 SetRoute(2, "b") 를 동시에 호출한다고 하자.
    1. T1: current = _map(비어 있음) → updated = {1:"a"}
    2. T2: current = _map(아직 비어 있음) → updated = {2:"b"}
    3. T1: _map = {1:"a"}
    4. T2: _map = {2:"b"} ← T1 의 {1:"a"} 가 사라짐 결과 테이블에 샤드 2 만 남고 샤드 1 라우트가 유실된다. volatile 은 가시성/재배열만 다루지 read-modify-write 를 원자화하지 않는다.
    • 재현: 오토스케일러와 운영 API 가 서로 다른 샤드를 거의 동시에 추가.
    • 근본 원인: "스냅샷 교체"가 이전에 읽은 스냅샷이 여전히 최신일 때만 유효해야 하는데 그 조건(compare)을 확인하지 않음.

수정안

Interlocked.CompareExchange 로 "내가 읽은 스냅샷이 그대로일 때만 교체"를 보장하고, 경합 시 재시도한다.

public void SetRoute(int shardId, string endpoint)
{
    while (true)
    {
        var current = _map;
        var updated = current.SetItem(shardId, endpoint);
        // current 가 그대로면 updated 로 교체, 아니면 실패한 이전 값 반환.
        if (Interlocked.CompareExchange(ref _map, updated, current) == current)
            return;
        // 다른 스레드가 먼저 바꿈 → 최신 스냅샷으로 재시도(수정을 다시 적용).
    }
}

(참고: Interlocked.CompareExchange(ref volatile T, ...) 사용을 위해 필드 선언에서 volatile 대신 접근 지점에서 Volatile.Read 를 쓰거나, CAS 자체가 가시성 배리어를 제공하므로 CAS 루프 모델에선 필드 volatile 없이도 안전하다.)

경합이 심하거나 갱신 로직이 복잡하면 쓰기만 락으로 직렬화(읽기는 여전히 무락)해도 된다:

private readonly object _writeGate = new();
public void SetRoute(int shardId, string endpoint)
{
    lock (_writeGate) { _map = _map.SetItem(shardId, endpoint); }
}

더 나은 설계

  • 쓰기 직렬화 + 무락 읽기: COW 의 정석은 "읽기 무락 / 쓰기는 단일 직렬화(락 또는 CAS 루프)". 읽기 성능을 지키면서 갱신 유실을 없앤다.
  • 배치 갱신: 여러 변경을 한 트랜잭션으로 모아 교체 횟수와 경합을 줄인다.
  • 버전/세대 번호를 스냅샷에 넣어 관측·디버깅과 stale 판정을 쉽게.

면접 포인트

  • "원자적 참조 교체"와 "read-modify-write 원자성"은 다르다 — 후자는 CAS/락 필요.
  • CAS 루프의 재시도 의미(내가 본 값이 바뀌었으면 최신으로 다시 계산).
  • volatile 이 해결하는 것(가시성/순서)과 못 하는 것(복합 갱신 원자성).