3. 긴급 점검 종료의 드레이닝 부재 (순서·대기·플러시 누락)
난이도 최상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 긴급 점검 공지 후 강제 종료까지의 드레이닝(graceful drain)
// ----------------------------------------------------------------------------
// 시나리오 (서버-서버/분산 · 운영/장애):
// - 긴급 점검을 위해 게임 서버를 내려야 한다. 공지 후 짧은 유예를 두고 종료한다.
// - 이 서버는 ① 처리 중인 요청(일부는 다른 존 서버로 RPC를 보내는 멀티스텝),
// ② 비동기 영속화 큐(_persist: 더티 상태를 백그라운드로 DB 저장),
// ③ 클러스터 등록(_cluster: 다른 서버들이 이 서버로 라우팅)을 가진다.
//
// 요구사항:
// - 종료 시 진행 중이던 작업이 "반쯤"만 적용된 채 끊기면 안 된다(예: 존 이전에서
// 원본에서 제거됐는데 대상에 추가 안 됨).
// - 영속화 큐에 쌓인 변경은 유실되면 안 된다(저장 후 종료).
// - 종료 중/후에 다른 서버가 이 서버로 새 작업을 보내면 안 된다.
//
// 과제:
// 이 종료 절차의 잠재적 문제를 모두 찾고, 처리 중 요청과 _persist 큐, _cluster 라우팅에
// 무슨 일이 일어나는지 설명하고, 올바른 드레이닝 순서/수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
using System;
using System.Collections.Generic;
public class GameServer
{
private volatile bool _running = true;
private readonly Listener _listener;
private readonly List<Session> _sessions;
private readonly PersistQueue _persist; // 비동기 영속화 큐
private readonly Cluster _cluster; // 서버-서버 라우팅 등록
public GameServer(Listener l, List<Session> s, PersistQueue p, Cluster c)
{ _listener = l; _sessions = s; _persist = p; _cluster = c; }
// 긴급 점검: 공지 후 종료를 호출
public void Shutdown()
{
_running = false; // (A) 처리 중단 플래그
_listener.Close(); // (B) 새 연결 차단
foreach (var s in _sessions) // (C) 모든 세션 즉시 끊기
s.CloseSocket();
Environment.Exit(0); // (D) 프로세스 종료
}
// 요청 처리(워커 여러 개). 일부는 다른 존 서버로 RPC를 보내는 멀티스텝.
public void HandleRequest(Session s, Request req)
{
if (!_running) return;
// step1: 로컬 상태 변경 → _persist.Enqueue(dirty)
// step2: 다른 존 서버로 RPC(존 이전 등) → 응답 대기
// step3: 로컬 커밋 / 세션에 결과 전송
// (멀티스텝 중간에 (C)/(D)가 끼어들 수 있다)
}
}
public class Listener { public void Close() { } }
public class Session { public void CloseSocket() { } }
public class PersistQueue { public void Enqueue(object dirty) { } /* 백그라운드 saver 가 비움 */ }
public class Cluster { public void Register() { } public void Deregister() { } }
public class Request { } 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 긴급 점검 공지 후 강제 종료까지의 드레이닝(graceful drain)
// ----------------------------------------------------------------------------
// 시나리오 (서버-서버/분산 · 운영/장애):
// - 긴급 점검을 위해 게임 서버를 내려야 한다. 공지 후 짧은 유예를 두고 종료한다.
// - 이 서버는 ① 처리 중인 요청(일부는 다른 존 서버로 RPC를 보내는 멀티스텝),
// ② 비동기 영속화 큐(persist: 백그라운드 saver 스레드가 더티 상태를 DB 저장),
// ③ 클러스터 등록(cluster: 다른 서버들이 이 서버로 라우팅)을 가진다.
//
// 요구사항:
// - 종료 시 진행 중이던 작업이 "반쯤"만 적용된 채 끊기면 안 된다.
// - 영속화 큐에 쌓인 변경은 유실되면 안 된다(저장 후 종료).
// - 종료 중/후에 다른 서버가 이 서버로 새 작업을 보내면 안 된다.
//
// 과제:
// 이 종료 절차의 잠재적 문제를 모두 찾고, 처리 중 요청과 persist 큐(백그라운드 스레드),
// cluster 라우팅, 그리고 스택 객체/소멸자에 무슨 일이 일어나는지 설명하고, 올바른
// 드레이닝 순서/수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <atomic>
#include <vector>
#include <thread>
#include <cstdlib>
struct Listener { void close() {} };
struct Session { void closeSocket() {} };
struct Request {};
// 백그라운드 saver 스레드가 큐를 비운다(소멸자에서 join+flush 한다고 가정)
class PersistQueue {
public:
void enqueue(int dirty) { (void)dirty; /* 큐에 적재 */ }
~PersistQueue() { /* flush + join saver thread */ }
};
struct Cluster { void registerSelf() {} void deregister() {} };
class GameServer {
public:
GameServer(Listener& l, std::vector<Session*>& s, PersistQueue& p, Cluster& c)
: _listener(l), _sessions(s), _persist(p), _cluster(c) {}
// 긴급 점검: 공지 후 종료를 호출
void shutdown() {
_running.store(false); // (A) 처리 중단 플래그
_listener.close(); // (B) 새 연결 차단
for (Session* s : _sessions) // (C) 모든 세션 즉시 끊기
s->closeSocket();
std::exit(0); // (D) 프로세스 종료
}
// 요청 처리(워커 여러 개). 일부는 다른 존 서버로 RPC를 보내는 멀티스텝.
void handleRequest(Session* s, Request& req) {
if (!_running.load()) return;
// step1: 로컬 상태 변경 → _persist.enqueue(dirty)
// step2: 다른 존 서버로 RPC(존 이전 등) → 응답 대기
// step3: 로컬 커밋 / 세션에 결과 전송
(void)s; (void)req;
}
private:
std::atomic<bool> _running{true};
Listener& _listener;
std::vector<Session*>& _sessions;
PersistQueue& _persist;
Cluster& _cluster;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.