feat: 명령어 요청 ID 처리 개선 및 상태 응답 기능 업데이트
This commit is contained in:
@@ -27,7 +27,10 @@ export class InMemoryTurnDaemonTransport implements TurnDaemonTransport {
|
|||||||
|
|
||||||
// 테스트용: 메모리 큐에 명령을 저장하고 requestId를 반환한다.
|
// 테스트용: 메모리 큐에 명령을 저장하고 requestId를 반환한다.
|
||||||
async sendCommand(command: TurnDaemonCommand): Promise<string> {
|
async sendCommand(command: TurnDaemonCommand): Promise<string> {
|
||||||
const requestId = command.type === 'getStatus' ? command.requestId : randomUUID();
|
const requestId =
|
||||||
|
command.type === 'getStatus' && command.requestId
|
||||||
|
? command.requestId
|
||||||
|
: randomUUID();
|
||||||
this.commands.push({
|
this.commands.push({
|
||||||
requestId,
|
requestId,
|
||||||
sentAt: new Date().toISOString(),
|
sentAt: new Date().toISOString(),
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ type RedisStreamReadResponse = Array<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
const buildCommandEnvelope = (command: TurnDaemonCommand): TurnDaemonCommandEnvelope => {
|
const buildCommandEnvelope = (command: TurnDaemonCommand): TurnDaemonCommandEnvelope => {
|
||||||
const requestId = command.type === 'getStatus' ? command.requestId : randomUUID();
|
const requestId =
|
||||||
|
command.type === 'getStatus' && command.requestId
|
||||||
|
? command.requestId
|
||||||
|
: randomUUID();
|
||||||
return {
|
return {
|
||||||
requestId,
|
requestId,
|
||||||
sentAt: new Date().toISOString(),
|
sentAt: new Date().toISOString(),
|
||||||
|
|||||||
@@ -1,89 +1,16 @@
|
|||||||
export type TurnDaemonState = 'idle' | 'running' | 'flushing' | 'paused' | 'stopping';
|
import type { TurnDaemonCommand, TurnDaemonEvent } from '@sammo-ts/common';
|
||||||
|
|
||||||
export type RunReason = 'schedule' | 'manual' | 'poke';
|
export type {
|
||||||
|
RunReason,
|
||||||
export interface TurnRunBudget {
|
TurnCheckpoint,
|
||||||
budgetMs: number;
|
TurnDaemonCommand,
|
||||||
maxGenerals: number;
|
TurnDaemonCommandResult,
|
||||||
catchUpCap: number;
|
TurnDaemonEvent,
|
||||||
}
|
TurnDaemonState,
|
||||||
|
TurnDaemonStatus,
|
||||||
export interface TurnCheckpoint {
|
TurnRunBudget,
|
||||||
turnTime: string;
|
TurnRunResult,
|
||||||
generalId?: number;
|
} from '@sammo-ts/common';
|
||||||
year: number;
|
|
||||||
month: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TurnRunResult {
|
|
||||||
lastTurnTime: string;
|
|
||||||
processedGenerals: number;
|
|
||||||
processedTurns: number;
|
|
||||||
durationMs: number;
|
|
||||||
partial: boolean;
|
|
||||||
checkpoint?: TurnCheckpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TurnDaemonStatus {
|
|
||||||
state: TurnDaemonState;
|
|
||||||
running: boolean;
|
|
||||||
paused: boolean;
|
|
||||||
lastError?: string;
|
|
||||||
lastRunAt?: string;
|
|
||||||
lastDurationMs?: number;
|
|
||||||
lastTurnTime?: string;
|
|
||||||
nextTurnTime?: string;
|
|
||||||
pendingReason?: RunReason;
|
|
||||||
queueDepth: number;
|
|
||||||
checkpoint?: TurnCheckpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TurnDaemonMutationCommand =
|
|
||||||
| { type: 'troopJoin'; generalId: number; troopId: number }
|
|
||||||
| { type: 'troopExit'; generalId: number };
|
|
||||||
|
|
||||||
// 턴 데몬 제어 요청은 Redis 스트림으로 전달한다.
|
|
||||||
export type TurnDaemonCommand =
|
|
||||||
| { type: 'run'; reason: RunReason; targetTime?: string; budget?: TurnRunBudget }
|
|
||||||
| { type: 'pause'; reason?: string }
|
|
||||||
| { type: 'resume'; reason?: string }
|
|
||||||
| { type: 'getStatus'; requestId: string }
|
|
||||||
| TurnDaemonMutationCommand;
|
|
||||||
|
|
||||||
export type TurnDaemonCommandResult =
|
|
||||||
| {
|
|
||||||
type: 'troopJoin';
|
|
||||||
ok: true;
|
|
||||||
generalId: number;
|
|
||||||
troopId: number;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'troopJoin';
|
|
||||||
ok: false;
|
|
||||||
generalId: number;
|
|
||||||
troopId: number;
|
|
||||||
reason: string;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'troopExit';
|
|
||||||
ok: true;
|
|
||||||
generalId: number;
|
|
||||||
wasLeader: boolean;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'troopExit';
|
|
||||||
ok: false;
|
|
||||||
generalId: number;
|
|
||||||
reason: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 턴 데몬 이벤트는 상태/실행 결과를 API 서버에 알려준다.
|
|
||||||
export type TurnDaemonEvent =
|
|
||||||
| { type: 'status'; requestId?: string; status: TurnDaemonStatus }
|
|
||||||
| { type: 'runStarted'; at: string; reason: RunReason }
|
|
||||||
| { type: 'runCompleted'; at: string; result: TurnRunResult }
|
|
||||||
| { type: 'runFailed'; at: string; error: string }
|
|
||||||
| { type: 'commandResult'; result: TurnDaemonCommandResult };
|
|
||||||
|
|
||||||
export interface TurnDaemonCommandEnvelope {
|
export interface TurnDaemonCommandEnvelope {
|
||||||
requestId: string;
|
requestId: string;
|
||||||
|
|||||||
@@ -228,10 +228,12 @@ export class TurnDaemonLifecycle {
|
|||||||
this.stopping = true;
|
this.stopping = true;
|
||||||
return;
|
return;
|
||||||
case 'getStatus': {
|
case 'getStatus': {
|
||||||
await this.commandResponder?.publishStatus(
|
if (command.requestId) {
|
||||||
command.requestId,
|
await this.commandResponder?.publishStatus(
|
||||||
this.getStatus()
|
command.requestId,
|
||||||
);
|
this.getStatus()
|
||||||
|
);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case 'run':
|
case 'run':
|
||||||
@@ -285,7 +287,7 @@ export class TurnDaemonLifecycle {
|
|||||||
} as TurnDaemonCommandResult;
|
} as TurnDaemonCommandResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.commandResponder) {
|
if (this.commandResponder && command.requestId) {
|
||||||
await this.commandResponder.publishCommandResult(
|
await this.commandResponder.publishCommandResult(
|
||||||
command.requestId,
|
command.requestId,
|
||||||
result
|
result
|
||||||
|
|||||||
@@ -1,78 +1,22 @@
|
|||||||
export type TurnDaemonState = 'idle' | 'running' | 'flushing' | 'paused' | 'stopping';
|
import type {
|
||||||
|
TurnCheckpoint,
|
||||||
|
TurnDaemonCommand,
|
||||||
|
TurnDaemonCommandResult,
|
||||||
|
TurnDaemonStatus,
|
||||||
|
TurnRunBudget,
|
||||||
|
TurnRunResult,
|
||||||
|
} from '@sammo-ts/common';
|
||||||
|
|
||||||
export type RunReason = 'schedule' | 'manual' | 'poke';
|
export type {
|
||||||
|
RunReason,
|
||||||
export interface TurnRunBudget {
|
TurnCheckpoint,
|
||||||
budgetMs: number;
|
TurnDaemonCommand,
|
||||||
maxGenerals: number;
|
TurnDaemonCommandResult,
|
||||||
catchUpCap: number;
|
TurnDaemonState,
|
||||||
}
|
TurnDaemonStatus,
|
||||||
|
TurnRunBudget,
|
||||||
export interface TurnCheckpoint {
|
TurnRunResult,
|
||||||
turnTime: string;
|
} from '@sammo-ts/common';
|
||||||
generalId?: number;
|
|
||||||
year: number;
|
|
||||||
month: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TurnRunResult {
|
|
||||||
lastTurnTime: string;
|
|
||||||
processedGenerals: number;
|
|
||||||
processedTurns: number;
|
|
||||||
durationMs: number;
|
|
||||||
partial: boolean;
|
|
||||||
checkpoint?: TurnCheckpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TurnDaemonStatus {
|
|
||||||
state: TurnDaemonState;
|
|
||||||
running: boolean;
|
|
||||||
paused: boolean;
|
|
||||||
lastError?: string;
|
|
||||||
lastRunAt?: string;
|
|
||||||
lastDurationMs?: number;
|
|
||||||
lastTurnTime?: string;
|
|
||||||
nextTurnTime?: string;
|
|
||||||
pendingReason?: RunReason;
|
|
||||||
queueDepth: number;
|
|
||||||
checkpoint?: TurnCheckpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TurnDaemonCommand =
|
|
||||||
| { type: 'run'; reason: RunReason; targetTime?: string; budget?: TurnRunBudget }
|
|
||||||
| { type: 'pause'; reason?: string }
|
|
||||||
| { type: 'resume'; reason?: string }
|
|
||||||
| { type: 'shutdown'; reason?: string }
|
|
||||||
| { type: 'getStatus'; requestId: string }
|
|
||||||
| { type: 'troopJoin'; requestId: string; generalId: number; troopId: number }
|
|
||||||
| { type: 'troopExit'; requestId: string; generalId: number };
|
|
||||||
|
|
||||||
export type TurnDaemonCommandResult =
|
|
||||||
| {
|
|
||||||
type: 'troopJoin';
|
|
||||||
ok: true;
|
|
||||||
generalId: number;
|
|
||||||
troopId: number;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'troopJoin';
|
|
||||||
ok: false;
|
|
||||||
generalId: number;
|
|
||||||
troopId: number;
|
|
||||||
reason: string;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'troopExit';
|
|
||||||
ok: true;
|
|
||||||
generalId: number;
|
|
||||||
wasLeader: boolean;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'troopExit';
|
|
||||||
ok: false;
|
|
||||||
generalId: number;
|
|
||||||
reason: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface TurnDaemonCommandHandler {
|
export interface TurnDaemonCommandHandler {
|
||||||
handle(command: TurnDaemonCommand): Promise<TurnDaemonCommandResult | null>;
|
handle(command: TurnDaemonCommand): Promise<TurnDaemonCommandResult | null>;
|
||||||
|
|||||||
@@ -10,3 +10,4 @@ export * from './util/RNG.js';
|
|||||||
export * from './util/RandUtil.js';
|
export * from './util/RandUtil.js';
|
||||||
export * from './util/TestRNG.js';
|
export * from './util/TestRNG.js';
|
||||||
export * from './util/sha512.js';
|
export * from './util/sha512.js';
|
||||||
|
export * from './turnDaemon/types.js';
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
export type TurnDaemonState =
|
||||||
|
| 'idle'
|
||||||
|
| 'running'
|
||||||
|
| 'flushing'
|
||||||
|
| 'paused'
|
||||||
|
| 'stopping';
|
||||||
|
|
||||||
|
export type RunReason = 'schedule' | 'manual' | 'poke';
|
||||||
|
|
||||||
|
export interface TurnRunBudget {
|
||||||
|
budgetMs: number;
|
||||||
|
maxGenerals: number;
|
||||||
|
catchUpCap: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TurnCheckpoint {
|
||||||
|
turnTime: string;
|
||||||
|
generalId?: number;
|
||||||
|
year: number;
|
||||||
|
month: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TurnRunResult {
|
||||||
|
lastTurnTime: string;
|
||||||
|
processedGenerals: number;
|
||||||
|
processedTurns: number;
|
||||||
|
durationMs: number;
|
||||||
|
partial: boolean;
|
||||||
|
checkpoint?: TurnCheckpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TurnDaemonStatus {
|
||||||
|
state: TurnDaemonState;
|
||||||
|
running: boolean;
|
||||||
|
paused: boolean;
|
||||||
|
lastError?: string;
|
||||||
|
lastRunAt?: string;
|
||||||
|
lastDurationMs?: number;
|
||||||
|
lastTurnTime?: string;
|
||||||
|
nextTurnTime?: string;
|
||||||
|
pendingReason?: RunReason;
|
||||||
|
queueDepth: number;
|
||||||
|
checkpoint?: TurnCheckpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TurnDaemonCommand =
|
||||||
|
| {
|
||||||
|
type: 'run';
|
||||||
|
reason: RunReason;
|
||||||
|
targetTime?: string;
|
||||||
|
budget?: TurnRunBudget;
|
||||||
|
}
|
||||||
|
| { type: 'pause'; reason?: string }
|
||||||
|
| { type: 'resume'; reason?: string }
|
||||||
|
| { type: 'shutdown'; reason?: string }
|
||||||
|
| { type: 'getStatus'; requestId?: string }
|
||||||
|
| { type: 'troopJoin'; requestId?: string; generalId: number; troopId: number }
|
||||||
|
| { type: 'troopExit'; requestId?: string; generalId: number };
|
||||||
|
|
||||||
|
export type TurnDaemonCommandResult =
|
||||||
|
| {
|
||||||
|
type: 'troopJoin';
|
||||||
|
ok: true;
|
||||||
|
generalId: number;
|
||||||
|
troopId: number;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'troopJoin';
|
||||||
|
ok: false;
|
||||||
|
generalId: number;
|
||||||
|
troopId: number;
|
||||||
|
reason: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'troopExit';
|
||||||
|
ok: true;
|
||||||
|
generalId: number;
|
||||||
|
wasLeader: boolean;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'troopExit';
|
||||||
|
ok: false;
|
||||||
|
generalId: number;
|
||||||
|
reason: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TurnDaemonEvent =
|
||||||
|
| { type: 'status'; requestId?: string; status: TurnDaemonStatus }
|
||||||
|
| { type: 'runStarted'; at: string; reason: RunReason }
|
||||||
|
| { type: 'runCompleted'; at: string; result: TurnRunResult }
|
||||||
|
| { type: 'runFailed'; at: string; error: string }
|
||||||
|
| { type: 'commandResult'; result: TurnDaemonCommandResult };
|
||||||
Reference in New Issue
Block a user