fix(simulation): make create_simulation idempotent to prevent duplicate sims

Creating a simulation was not idempotent: each POST /api/simulation/create
minted a fresh sim_<uuid>. The frontend wraps createSimulation in
requestWithRetry(..., 3, 1000), so a slow/timed-out first request triggers a
retry that creates a second identical simulation for the same project+graph
(observed: two sims created ~2s apart).

Guard against this by reusing an existing, not-yet-run simulation for the same
project_id + graph_id (and matching platform flags) when status is
CREATED/PREPARING/READY and current_round == 0. A force_new=True escape hatch
preserves the ability to intentionally create a fresh simulation.

Verified: repeated /create calls now return the same simulation_id instead of
spawning duplicates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
FeelRatchanons 2026-06-13 10:22:11 +07:00
parent 96096ea0ff
commit 6686011f91
1 changed files with 26 additions and 2 deletions

View File

@ -197,19 +197,43 @@ class SimulationManager:
graph_id: str,
enable_twitter: bool = True,
enable_reddit: bool = True,
force_new: bool = False,
) -> SimulationState:
"""
创建新的模拟
Args:
project_id: 项目ID
graph_id: Zep图谱ID
enable_twitter: 是否启用Twitter模拟
enable_reddit: 是否启用Reddit模拟
force_new: 强制新建跳过幂等复用默认False
Returns:
SimulationState
"""
# 幂等防重复:若已存在同一 project+graph 且尚未运行的模拟,则直接复用,
# 避免前端重试requestWithRetry或重复提交导致创建出多个相同的模拟。
if not force_new:
reusable_statuses = (
SimulationStatus.CREATED,
SimulationStatus.PREPARING,
SimulationStatus.READY,
)
for existing in self.list_simulations(project_id=project_id):
if (
existing.graph_id == graph_id
and existing.enable_twitter == enable_twitter
and existing.enable_reddit == enable_reddit
and existing.current_round == 0
and existing.status in reusable_statuses
):
logger.info(
f"复用未运行的模拟(幂等防重复): {existing.simulation_id}, "
f"project={project_id}, graph={graph_id}, status={existing.status.value}"
)
return existing
import uuid
simulation_id = f"sim_{uuid.uuid4().hex[:12]}"