Run discards on the state-driven coordinator

This commit is contained in:
Ian Gulliver
2026-10-21 11:53:37 -07:00
parent f3a0e28763
commit 1b5f42a01a
8 changed files with 141 additions and 191 deletions
+31 -84
View File
@@ -134,7 +134,7 @@ func (m *Manager) searchClosedText(ctx context.Context, projectID, query string,
}
func (m *Manager) Discard(ctx context.Context, task api.Task, actor string) (api.Task, error) {
state, err := m.store.RequestDiscard(ctx, task.Id, actor)
err := m.store.RequestDiscard(ctx, task.Id, actor)
switch {
case errors.Is(err, store.ErrNotFound):
return api.Task{}, fmt.Errorf("%w: task %s is closed", ErrConflict, task.Id)
@@ -149,94 +149,74 @@ func (m *Manager) Discard(ctx context.Context, task api.Task, actor string) (api
case err != nil:
return api.Task{}, err
}
if state == store.DiscardClosing {
go m.finishDiscard(task.Id)
} else {
go m.startDiscard(task.Id)
}
return m.store.GetTask(ctx, task.Id)
}
func (m *Manager) startDiscard(taskID string) {
ctx := context.Background()
started, err := m.launchDiscard(ctx, taskID)
func (m *Manager) startDiscard(ctx context.Context, taskID string) {
err := m.beginDiscard(ctx, taskID)
if err == nil {
if started {
slog.Info("discard turn started", "task", taskID)
}
return
}
slog.Error("start discard turn", "task", taskID, "err", err)
err = m.store.CloseDiscard(ctx, taskID)
if err != nil {
slog.Error("close discard", "task", taskID, "err", err)
return
}
m.finishDiscard(taskID)
m.closeDiscard(ctx, taskID)
}
func (m *Manager) launchDiscard(ctx context.Context, taskID string) (bool, error) {
func (m *Manager) closeDiscard(ctx context.Context, taskID string) {
err := m.store.CloseDiscard(ctx, taskID)
if err != nil {
slog.Error("close discard", "task", taskID, "err", err)
}
m.poke()
}
func (m *Manager) beginDiscard(ctx context.Context, taskID string) error {
task, err := m.store.GetTask(ctx, taskID)
if err != nil {
return false, err
return err
}
sess, found, err := m.liveSession(ctx, taskID)
if err != nil {
return false, err
return err
}
if !found {
creator, err := m.store.GetUser(ctx, task.CreatedBy)
if err != nil {
return false, err
return err
}
sess, err = m.newSession(ctx, task, creator, sess.Member, "")
if err != nil {
return false, err
return err
}
}
err = m.store.StartDiscardTurn(ctx, taskID, sess.Id, "byre")
if errors.Is(err, store.ErrStateChanged) {
return false, nil
return nil
}
if err != nil {
return false, err
return err
}
_, err = m.launch(ctx, sess, task, "byre", discardPrompt, "", "", nil)
return err == nil, err
slog.Info("discard turn started", "task", taskID)
go func() {
ctx := context.Background()
_, err := m.launch(ctx, sess, task, "byre", discardPrompt, "", "", nil)
if err == nil {
return
}
slog.Error("launch discard turn", "task", taskID, "err", err)
m.closeDiscard(ctx, taskID)
}()
return nil
}
func (m *Manager) finishDiscard(taskID string) {
m.mu.Lock()
if m.finishing[taskID] {
m.mu.Unlock()
return
}
m.finishing[taskID] = true
m.mu.Unlock()
defer func() {
m.mu.Lock()
delete(m.finishing, taskID)
m.mu.Unlock()
}()
ctx := context.Background()
err := m.discardNow(ctx, taskID)
if err == nil {
return
}
slog.Error("finish discard", "task", taskID, "err", err)
sessions, serr := m.store.ListSessions(ctx, taskID)
if serr != nil {
slog.Error("list sessions for discard error", "task", taskID, "err", serr)
return
}
task, terr := m.store.GetTask(ctx, taskID)
if terr != nil {
slog.Error("load task for discard error", "task", taskID, "err", terr)
return
}
m.fileTaskError(ctx, task, sessions, "Discarding the task failed: "+err.Error()+". Discard again to retry; byre also retries when it next starts.")
m.fileError(ctx, taskID, "Discarding the task failed: "+err.Error()+". byre retries when it next starts.")
}
func (m *Manager) discardNow(ctx context.Context, taskID string) error {
@@ -264,36 +244,3 @@ func (m *Manager) discardNow(ctx context.Context, taskID string) error {
_, err = m.store.DiscardTask(ctx, task.Id, discard.By)
return err
}
func (m *Manager) recoverDiscards(ctx context.Context) error {
tasks, err := m.store.PendingDiscards(ctx)
if err != nil {
return err
}
for _, t := range tasks {
discard, err := m.store.GetDiscard(ctx, t.Id)
if err != nil {
return err
}
switch discard.State {
case store.DiscardHeld:
go m.startDiscard(t.Id)
case store.DiscardClosing:
go m.finishDiscard(t.Id)
case store.DiscardRunning:
sess, err := m.store.GetSession(ctx, discard.Session)
if err != nil {
return err
}
if sess.State == api.AgentSessionStateRunning {
continue
}
err = m.store.CloseDiscard(ctx, t.Id)
if err != nil {
return err
}
go m.finishDiscard(t.Id)
}
}
return nil
}