13. 리전 간 벽시계 타임스탬프로 이벤트 순서를 재구성하는 결함
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// 시나리오 (서버-서버): 여러 리전(지역)에 배치된 게임 서버들이 각각 발생시킨
// 이벤트(예: 길드 자원 기여, 크로스 리전 경매 입찰)를 중앙 집계 서버가 모아
// 순서대로 재구성해 처리해야 한다.
//
// 각 리전 서버는 이벤트를 생성할 때 자신의 시스템 시각(wall-clock)으로
// 타임스탬프를 찍어 보낸다. 중앙 집계 서버는 도착한 이벤트들을 이 타임스탬프
// 기준으로 정렬해 "일어난 순서"로 간주하고 처리한다.
//
// 서로 다른 리전의 서버 시각은 NTP 로 동기화되어 있지만, 네트워크 상황에
// 따라 수십~수백 밀리초 수준의 드리프트(오차)가 생길 수 있다.
//
// 요구사항:
// - 실제 인과관계가 있는 이벤트(예: "입찰 A 가 있었기 때문에 그에 대한 응답
// 이벤트 B 가 발생")는 그 인과관계에 맞는 순서로 처리되어야 한다.
// - 리전 간 시계 오차가 있더라도 인과관계가 있는 이벤트의 순서가 뒤바뀌면
// 안 된다.
//
// 과제: (A)(B) 지점을 검토해 위 요구사항이 깨지는 경로를 찾고 수정하라.
using System;
using System.Collections.Generic;
using System.Linq;
public sealed class RegionEvent
{
public string RegionId;
public string EventId;
public DateTime WallClockTimestamp; // 발생 리전의 시스템 시각
public string Description;
}
public sealed class GlobalEventAggregator
{
private readonly List<RegionEvent> _buffer = new();
// 각 리전 서버로부터 이벤트를 수신할 때마다 호출된다.
public void OnEventReceived(RegionEvent evt)
{
_buffer.Add(evt); // (A) 수신 순서와 무관하게 일단 버퍼에 쌓는다.
}
// 일정 주기로 호출되어, 지금까지 쌓인 이벤트를 "일어난 순서"로 처리한다.
public List<RegionEvent> DrainInOrder()
{
// (B) 리전별 시스템 시각(wall-clock) 만으로 전역 순서를 재구성한다.
var ordered = _buffer.OrderBy(e => e.WallClockTimestamp).ToList();
_buffer.Clear();
return ordered;
}
} 결함 코드 · C++
// ============================================================================
// 시나리오 (서버-서버)
// ----------------------------------------------------------------------------
// 여러 리전(지역)에 배치된 게임 서버들이 각각 발생시킨 이벤트(예: 길드
// 자원 기여, 크로스 리전 경매 입찰)를 중앙 집계 서버가 모아 순서대로
// 재구성해 처리해야 한다.
//
// 각 리전 서버는 이벤트를 생성할 때 자신의 시스템 시각(wall-clock)으로
// 타임스탬프를 찍어 보낸다. 중앙 집계 서버는 도착한 이벤트들을 이
// 타임스탬프 기준으로 정렬해 "일어난 순서"로 간주하고 처리한다.
//
// 서로 다른 리전의 서버 시각은 NTP 로 동기화되어 있지만, 네트워크 상황에
// 따라 수십~수백 밀리초 수준의 드리프트(오차)가 생길 수 있다.
//
// 요구사항:
// - 실제 인과관계가 있는 이벤트(예: "입찰 A 가 있었기 때문에 그에 대한
// 응답 이벤트 B 가 발생")는 그 인과관계에 맞는 순서로 처리되어야 한다.
// - 리전 간 시계 오차가 있더라도 인과관계가 있는 이벤트의 순서가
// 뒤바뀌면 안 된다.
//
// 과제: (A)(B) 지점을 검토해 위 요구사항이 깨지는 경로를 찾고 수정하라.
// ============================================================================
#include <algorithm>
#include <chrono>
#include <string>
#include <vector>
struct RegionEvent
{
std::string regionId;
std::string eventId;
std::chrono::system_clock::time_point wallClockTimestamp; // 발생 리전의 시스템 시각
std::string description;
};
class GlobalEventAggregator
{
public:
// 각 리전 서버로부터 이벤트를 수신할 때마다 호출된다.
void OnEventReceived(const RegionEvent& evt)
{
buffer_.push_back(evt); // (A) 수신 순서와 무관하게 일단 버퍼에 쌓는다.
}
// 일정 주기로 호출되어, 지금까지 쌓인 이벤트를 "일어난 순서"로 처리한다.
std::vector<RegionEvent> DrainInOrder()
{
// (B) 리전별 시스템 시각(wall-clock) 만으로 전역 순서를 재구성한다.
std::vector<RegionEvent> ordered = buffer_;
std::sort(ordered.begin(), ordered.end(),
[](const RegionEvent& a, const RegionEvent& b) {
return a.wallClockTimestamp < b.wallClockTimestamp;
});
buffer_.clear();
return ordered;
}
private:
std::vector<RegionEvent> buffer_;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.