16. 성급한 장애 감지와 플래핑으로 인한 불필요한 리밸런싱 (C#)

난이도 상 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C#
// 시나리오: 게임 노드 클러스터의 장애 감지와 세션 리밸런싱 (C#)
//
// 코디네이터는 각 게임 노드로부터 하트비트를 받는다. 노드가 죽었다고 판단되면
// 그 노드가 담당하던 세션(존/방)을 살아있는 노드로 재배치(리밸런싱)한다.
//
// 요구사항:
//  - 일시적 네트워크 지연/지터로 하트비트가 한두 번 늦는 것으로 노드를 죽었다고 판단하면 안 된다.
//  - 리밸런싱은 비싸고 파괴적이므로(세션 이관·재접속 유발) 정말 죽었을 때만 해야 한다.
//  - 하트비트가 늦었다가 곧 돌아온 노드는 계속 정상으로 취급해야 한다.
//
// HeartbeatMonitor 는 1초마다 Tick() 이 호출된다. 표시된 지점 (A)(B)(C).

using System;
using System.Collections.Generic;

public sealed class NodeInfo
{
    public string NodeId = "";
    public DateTime LastHeartbeat;
    public bool Alive = true;
}

public sealed class HeartbeatMonitor
{
    private readonly Dictionary<string, NodeInfo> _nodes = new();
    private readonly TimeSpan _timeout = TimeSpan.FromSeconds(2);
    private readonly Action<string> _rebalanceFrom;

    public HeartbeatMonitor(Action<string> rebalanceFrom) { _rebalanceFrom = rebalanceFrom; }

    public void OnHeartbeat(string nodeId)
    {
        if (!_nodes.TryGetValue(nodeId, out var n))
            _nodes[nodeId] = n = new NodeInfo { NodeId = nodeId };
        n.LastHeartbeat = DateTime.UtcNow;   // 갱신
    }

    public void Tick()
    {
        DateTime now = DateTime.UtcNow;
        foreach (var n in _nodes.Values)
        {
            // (A) 마지막 하트비트 이후 timeout 초과면 죽음으로 판정
            bool timedOut = (now - n.LastHeartbeat) > _timeout;

            if (timedOut && n.Alive)
            {
                n.Alive = false;              // (B) 죽음 표시
                _rebalanceFrom(n.NodeId);     // (C) 즉시 리밸런싱 트리거
            }
            else if (!timedOut && !n.Alive)
            {
                n.Alive = true;               // 하트비트 돌아오면 다시 살림
            }
        }
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.