feat: support durable task cancellation without foreign keys

This commit is contained in:
2026-09-17 17:03:23 +08:00
parent 6805506450
commit 3ce7d2c104
48 changed files with 1642 additions and 164 deletions

View File

@@ -248,6 +248,7 @@ func (h *WorkerHub) ServeHTTP(w http.ResponseWriter, r *http.Request) {
draining := welcome.Draining
eligible := false
leases := map[core.ID]time.Time{}
cancelSent := map[core.ID]bool{}
ticker := time.NewTicker(h.poll)
defer ticker.Stop()
for {
@@ -332,6 +333,36 @@ func (h *WorkerHub) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
case <-ticker.C:
// 取消与领取容量、排空无关;通知丢失后仍可由 Resume/Renew 核对持久化意图。
if input.CancellationNotifications && len(leases) > 0 {
call, stop := context.WithTimeout(ctx, 8*time.Second)
items, err := h.service.Cancellations(call, token, binding)
stop()
if err != nil {
if fault.Code(err) == "STALE_SESSION" || fault.Code(err) == "UNAUTHENTICATED" || fault.Code(err) == "PERMISSION_DENIED" {
send(workerFrame{Type: "error", Error: publicWorkerError(err)}, true)
return
}
} else {
active := make(map[core.ID]bool, len(items))
for _, item := range items {
active[item.AttemptID] = true
if cancelSent[item.AttemptID] || len(outgoing) >= 4 {
continue
}
raw, err := jsonwire.Marshal(item)
if err != nil || !send(workerFrame{Type: "task.cancel", Body: raw}, false) {
return
}
cancelSent[item.AttemptID] = true
}
for id := range cancelSent {
if !active[id] {
delete(cancelSent, id)
}
}
}
}
// 本地记录仅减少空轮询;SQL 仍原子验证总容量、队列额度和有效会话。
now := time.Now()
for attempt, until := range leases {