fix: handle restore rollback and spawn id collisions
This commit is contained in:
parent
e66988ab77
commit
fcb3aec988
|
|
@ -101,6 +101,11 @@ func New(d Deps) *Manager {
|
|||
// step eagerly rolls back the steps that already succeeded.
|
||||
func (m *Manager) Spawn(ctx context.Context, cfg ports.SpawnConfig) (domain.Session, error) {
|
||||
id := m.newID(cfg)
|
||||
if _, ok, err := m.store.Get(ctx, id); err != nil {
|
||||
return domain.Session{}, fmt.Errorf("spawn %s: check existing: %w", id, err)
|
||||
} else if ok {
|
||||
return domain.Session{}, fmt.Errorf("spawn %s: already exists", id)
|
||||
}
|
||||
|
||||
ws, err := m.workspace.Create(ctx, ports.WorkspaceConfig{
|
||||
ProjectID: cfg.ProjectID,
|
||||
|
|
@ -315,6 +320,9 @@ func (m *Manager) Restore(ctx context.Context, id domain.SessionID) (domain.Sess
|
|||
}
|
||||
if err := m.lcm.OnSpawnCompleted(ctx, id, outcome); err != nil {
|
||||
m.rollbackRuntime(ctx, handle)
|
||||
if revertErr := m.lcm.OnSpawnInitiated(ctx, rec); revertErr != nil {
|
||||
return domain.Session{}, fmt.Errorf("restore %s: revert after spawn completed failure: %w (original error: %v)", id, revertErr, err)
|
||||
}
|
||||
return domain.Session{}, fmt.Errorf("restore %s: on spawn completed: %w", id, err)
|
||||
}
|
||||
return m.Get(ctx, id)
|
||||
|
|
|
|||
|
|
@ -121,6 +121,32 @@ func TestSpawn_RuntimeCreateFailure_RollsBack(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSpawn_ExistingSessionIDRejectedBeforeWork(t *testing.T) {
|
||||
h := newHarness("sess-1")
|
||||
ctx := context.Background()
|
||||
if err := h.store.Upsert(ctx, domain.SessionRecord{
|
||||
ID: "sess-1",
|
||||
ProjectID: testProject,
|
||||
Lifecycle: lc(domain.SessionWorking, domain.ReasonTaskInProgress, domain.PRNone, ""),
|
||||
}); err != nil {
|
||||
t.Fatalf("seed existing row: %v", err)
|
||||
}
|
||||
|
||||
_, err := h.sm.Spawn(ctx, spawnCfg())
|
||||
if err == nil {
|
||||
t.Fatal("spawn: want error for existing session id, got nil")
|
||||
}
|
||||
if len(h.workspace.created) != 0 {
|
||||
t.Error("workspace should not be created when session id already exists")
|
||||
}
|
||||
if len(h.runtime.created) != 0 {
|
||||
t.Error("runtime should not be created when session id already exists")
|
||||
}
|
||||
if h.log.indexOf("OnSpawnInitiated") != -1 || h.log.indexOf("OnSpawnCompleted") != -1 {
|
||||
t.Error("LCM should not be called when session id already exists")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpawn_OnSpawnCompletedFailure_RoutesOrphanToErrored(t *testing.T) {
|
||||
h := newHarness("sess-1")
|
||||
ctx := context.Background()
|
||||
|
|
@ -457,6 +483,11 @@ func TestRestore_OnSpawnCompletedFailure_RollsBackRuntime(t *testing.T) {
|
|||
t.Fatal("restore: want error, got nil")
|
||||
}
|
||||
|
||||
rec, _, _ := h.store.Get(ctx, "sess-1")
|
||||
if got := rec.Lifecycle.Session; got.State != domain.SessionTerminated || got.Reason != domain.ReasonManuallyKilled {
|
||||
t.Fatalf("restore failure should restore terminal lifecycle, got %+v", got)
|
||||
}
|
||||
|
||||
// The runtime created during restore is torn back down so no process is
|
||||
// stranded; the workspace is left intact (it holds the agent's prior work).
|
||||
if len(h.runtime.destroyed) != destroyedBefore+1 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue