이벤트 관리 명세
개요
이벤트 관리 명세는, TimeMachine이 시간 변경 시 발생시키는 이벤트의 형식, 처리 방법, 이벤트 흐름에 대한 상세 내용을 정의합니다. 이 명세는 가상 시간 변경 시 시스템 전체에 걸쳐 일관된 데이터와 동작을 보장하기 위한 것입니다.
이벤트 유형
TimeMachine에서 발생하는 주요 이벤트 유형은 다음과 같습니다:
| 이벤트 유형 | 설명 | 중요도 |
|---|---|---|
TIME_CHANGED | 시스템 가상 시간이 변경됨 | 높음 |
USER_TIME_CHANGED | 특정 사용자의 가상 시간이 변경됨 | 중간 |
TIME_RESET | 시스템 시간이 실제 시간으로 재설정됨 | 높음 |
TIMEMACHINE_ENABLED | TimeMachine 기능이 활성화됨 | 낮음 |
TIMEMACHINE_DISABLED | TimeMachine 기능이 비활성화됨 | 낮음 |
ROLLBACK_INITIATED | 데이터 롤백이 시작됨 | 높음 |
ROLLBACK_COMPLETED | 데이터 롤백이 완료됨 | 중간 |
ROLLBACK_FAILED | 데이터 롤백이 실패함 | 높음 |
이벤트 구조
1. 공통 이벤트 구조
모든 TimeMachine 이벤트는 다음과 같은 공통 구조를 갖습니다:
interface BaseTimeMachineEvent {
eventId: string; // 고유한 이벤트 ID (UUID)
eventType: string; // 이벤트 유형
source: string; // 이벤트 소스 (일반적으로 'time-machine')
timestamp: number; // 이벤트 발생 시간 (Epoch milliseconds)
correlationId?: string; // 연관된 요청 ID
userId: string; // 이벤트를 트리거한 사용자 ID
dataVersion: string; // 이벤트 데이터 버전 (예: '1.0')
}
2. 시간 변경 이벤트
interface TimeChangedEvent extends BaseTimeMachineEvent {
eventType: 'TIME_CHANGED';
data: {
previousTime: number; // 이전 가상 시간 (Epoch milliseconds)
newTime: number; // 새 가상 시간 (Epoch milliseconds)
reason?: string; // 시간 변경 이유
};
}
3. 사용자 시간 변경 이벤트
interface UserTimeChangedEvent extends BaseTimeMachineEvent {
eventType: 'USER_TIME_CHANGED';
data: {
targetUserId: string; // 가상 시간이 변경된 사용자 ID
previousTime: number; // 이전 가상 시간 (Epoch milliseconds)
newTime: number; // 새 가상 시간 (Epoch milliseconds)
reason?: string; // 시간 변경 이유
};
}
4. 롤백 관련 이벤트
interface RollbackEvent extends BaseTimeMachineEvent {
eventType: 'ROLLBACK_INITIATED' | 'ROLLBACK_COMPLETED' | 'ROLLBACK_FAILED';
data: {
rollbackId: string; // 롤백 작업 ID
targetUserId?: string; // 특정 사용자 롤백인 경우
previousTime: number; // 이전 가상 시간 (Epoch milliseconds)
newTime: number; // 새 가상 시간 (Epoch milliseconds)
affectedDataTypes: string[]; // 영향을 받은 데이터 유형
errorMessage?: string; // 롤백 실패 시 오류 메시지
};
}
이벤트 발행 및 구독
1. 발행 메커니즘
TimeMachine은 GCP Pub/Sub을 사용하여 이벤트를 발행합니다:
- 이벤트 발생 시, 이벤트 객체를 생성하고 유효성 검사
- 이벤트를 JSON으로 직렬화하여 메시지 페이로드로 사용
- 적절한 GCP Pub/Sub 토픽에 메시지 발행
- 메시지 발행 상태 모니터링 및 기록
// 예시 - 이벤트 발행기
async publishEvent(event: BaseTimeMachineEvent): Promise<void> {
const topicName = this.getTopicForEvent(event.eventType);
const messageId = await this.pubSubClient
.topic(topicName)
.publish(Buffer.from(JSON.stringify(event)));
await this.eventRepository.recordEvent({
eventId: event.eventId,
eventType: event.eventType,
messageId,
payload: event,
publishedAt: new Date(),
status: 'PUBLISHED',
});
}
2. 구독 메커니즘
다른 서비스가 TimeMachine 이벤트를 처리하는 방법:
- 해당 Pub/Sub 토픽에 구독
- 수신된 이벤트 메시지 역직렬화
- 이벤트 유형 및 데이터 유효성 검사
- 적절한 이벤트 핸들러로 라우팅
- 이벤트 처리 및 결과 기록
// 예시 - 이벤트 구독자
@PubSubSubscription('time-machine-events')
async handleTimeMachineEvent(
message: PubSubMessage,
ack: () => void,
): Promise<void> {
try {
const event = JSON.parse(message.data.toString()) as BaseTimeMachineEvent;
// 이벤트 유형에 따른 핸들러 호출
switch (event.eventType) {
case 'TIME_CHANGED':
await this.handleTimeChanged(event as TimeChangedEvent);
break;
case 'USER_TIME_CHANGED':
await this.handleUserTimeChanged(event as UserTimeChangedEvent);
break;
// 기타 이벤트 처리
}
// 메시지 확인 처리
ack();
} catch (error) {
// 오류 발생 시 처리 로직
console.error('Event processing error:', error);
// 재시도 가능한 오류인 경우 확인하지 않음
if (this.isRetryableError(error)) {
return;
}
// 복구 불가능한 오류인 경우 메시지 확인 처리
ack();
}
}
이벤트 처리 정책
1. 멱등성
모든 이벤트 핸들러는 멱등성을 보장해야 합니다. 즉, 동일한 이벤트가 여러 번 처리되더라도 결과는 동일해야 합니다:
- 이벤트 ID를 사용하여 중복 처리 방지
- 작업 수행 전 상태 확인으로 불필요한 중복 작업 방지
- 상태 변경은 원자적 연산으로 구현
2. 순서 보장
시간 변경 이벤트는 순서에 영향을 받을 수 있으므로, 처리 순서를 보장해야 합니다:
- 이벤트 타임스탬프 기반 정렬
- 동일 사용자에 대한 이벤트는 순차 처리
- 이전 이벤트 처리 완료 확인 후 다음 이벤트 처리
3. 오류 처리 및 재시도
이벤트 처리 중 발생할 수 있는 오류에 대한 정책:
- 일시적 오류는 자동 재시도 (지수 백오프 적용)
- 최대 재시도 횟수 (5회) 초과 시 실패 큐로 이동
- 복구 불가능한 오류는 즉시 실패 처리 및 알림
- 모든 실패한 이벤트는 수동 재시도 가능
이벤트 이력 관리
시스템은 모든 이벤트 발행 및 처리 이력을 저장하고 관리합니다:
1. 이벤트 이력 스키마
// Prisma 스키마
model EventHistory {
id String @id @default(uuid())
eventId String @unique // 이벤트 고유 ID
eventType String // 이벤트 유형
messageId String? // GCP Pub/Sub 메시지 ID
payload Json // 이벤트 페이로드 (JSON)
publishedAt DateTime // 발행 시간
status String // 상태: PUBLISHED, DELIVERY_FAILED, PROCESSED
retryCount Int @default(0) // 재시도 횟수
lastRetryAt DateTime? // 마지막 재시도 시간
errorMessage String? // 실패 시 오류 메시지
processingTime Int? // 처리 소요 시간 (밀리초)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([eventType])
@@index([status])
@@index([publishedAt])
}
2. 이벤트 이력 관리 기능
- 이벤트 발행 및 상태 기록
- 실패한 이벤트 조회 및 분석
- 수동 재시도 기능
- 이벤트 처리 성능 모니터링
- 이벤트 이력 보관 및 보관 정책 (30일)
실패한 이벤트 처리
1. 실패 감지
이벤트 처리 실패는 다음과 같은 상황에서 발생할 수 있습니다:
- 발행 서비스와 Pub/Sub 간 연결 문제
- 구독 서비스의 내부 오류
- 유효하지 않은 이벤트 데이터
- 처리 중 시스템 리소스 부족
2. 수동 재시도 프로세스
관리자는 실패한 이벤트를 다음과 같이 재시도할 수 있습니다:
- 실패한 이벤트 목록 조회
- 재시도할 이벤트 선택
- 이벤트 상태 및 데이터 검증
- 이벤트 수동 재발행 또는 콜백 처리
API 명세
이벤트 관리 API의 상세 명세는 이벤트 관리 API 문서를 참조하세요.