feat: implement input event system with durable command handling
- Introduced InputEvent model with status tracking (PENDING, PROCESSING, SUCCEEDED, FAILED) and unique request IDs. - Added DatabaseTurnDaemonTransport for sending commands and handling idempotency. - Implemented executeInputEvent function to manage input event lifecycle and error handling. - Created DatabaseTurnDaemonCommandQueue for managing command processing and lease recovery. - Enhanced turn daemon lifecycle to support atomic command execution and error recovery. - Added tests for input event atomicity, command queuing, and error handling scenarios.
This commit is contained in:
@@ -44,6 +44,39 @@ enum DiplomacyLetterState {
|
||||
REPLACED
|
||||
}
|
||||
|
||||
enum InputEventStatus {
|
||||
PENDING
|
||||
PROCESSING
|
||||
SUCCEEDED
|
||||
FAILED
|
||||
}
|
||||
|
||||
enum InputEventTarget {
|
||||
API
|
||||
ENGINE
|
||||
}
|
||||
|
||||
model InputEvent {
|
||||
sequence BigInt @id @default(autoincrement())
|
||||
requestId String @unique @map("request_id")
|
||||
target InputEventTarget
|
||||
eventType String @map("event_type")
|
||||
payload Json @default(dbgenerated("'{}'::jsonb"))
|
||||
actorUserId String? @map("actor_user_id")
|
||||
status InputEventStatus @default(PENDING)
|
||||
result Json?
|
||||
error String?
|
||||
attempts Int @default(0)
|
||||
lockedBy String? @map("locked_by")
|
||||
leaseUntil DateTime? @map("lease_until")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
processingAt DateTime? @map("processing_at")
|
||||
completedAt DateTime? @map("completed_at")
|
||||
|
||||
@@index([target, status, sequence])
|
||||
@@map("input_event")
|
||||
}
|
||||
|
||||
model WorldState {
|
||||
id Int @id @default(autoincrement())
|
||||
scenarioCode String @map("scenario_code")
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
CREATE TYPE "InputEventStatus" AS ENUM ('PENDING', 'PROCESSING', 'SUCCEEDED', 'FAILED');
|
||||
CREATE TYPE "InputEventTarget" AS ENUM ('API', 'ENGINE');
|
||||
|
||||
CREATE TABLE "input_event" (
|
||||
"sequence" BIGSERIAL NOT NULL,
|
||||
"request_id" TEXT NOT NULL,
|
||||
"target" "InputEventTarget" NOT NULL,
|
||||
"event_type" TEXT NOT NULL,
|
||||
"payload" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
"actor_user_id" TEXT,
|
||||
"status" "InputEventStatus" NOT NULL DEFAULT 'PENDING',
|
||||
"result" JSONB,
|
||||
"error" TEXT,
|
||||
"attempts" INTEGER NOT NULL DEFAULT 0,
|
||||
"locked_by" TEXT,
|
||||
"lease_until" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"processing_at" TIMESTAMP(3),
|
||||
"completed_at" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "input_event_pkey" PRIMARY KEY ("sequence")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "input_event_request_id_key" ON "input_event"("request_id");
|
||||
CREATE INDEX "input_event_target_status_sequence_idx" ON "input_event"("target", "status", "sequence");
|
||||
@@ -25,4 +25,5 @@ export interface DatabaseClient {
|
||||
inheritanceLog: GamePrisma.InheritanceLogDelegate;
|
||||
inheritanceResult: GamePrisma.InheritanceResultDelegate;
|
||||
inheritanceUserState: GamePrisma.InheritanceUserStateDelegate;
|
||||
inputEvent: GamePrisma.InputEventDelegate;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user