fix(docgov): reclaim orphan squad leases and align run_id
Use task run_id as doc lease holder so release tracks execution, and reclaim squad locks when the holder run is missing or no longer running. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -538,7 +538,10 @@ func (s *Service) ExecuteTask(ctx context.Context, req TaskRunRequest) (*TaskRun
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 通过权限检查后获取小队编辑租约(提案拦截路径不持锁)
|
// 通过权限检查后获取小队编辑租约(提案拦截路径不持锁)
|
||||||
leaseHolderID = fmt.Sprintf("run_%d", time.Now().UnixNano())
|
leaseHolderID = strings.TrimSpace(req.RunID)
|
||||||
|
if leaseHolderID == "" {
|
||||||
|
leaseHolderID = fmt.Sprintf("run_%d", time.Now().UnixNano())
|
||||||
|
}
|
||||||
leaseTTL := req.Timeout
|
leaseTTL := req.Timeout
|
||||||
if leaseTTL <= 0 && s.cfg != nil {
|
if leaseTTL <= 0 && s.cfg != nil {
|
||||||
leaseTTL = s.cfg.AgentTimeout
|
leaseTTL = s.cfg.AgentTimeout
|
||||||
|
|||||||
@@ -67,7 +67,14 @@ func (e *GovernanceEngine) AcquireLease(ctx context.Context, req LeaseAcquireReq
|
|||||||
|
|
||||||
if doc.HasActiveLease(now) {
|
if doc.HasActiveLease(now) {
|
||||||
sameHolder := doc.LeaseHolderType == req.HolderType && doc.LeaseHolderID == req.HolderID
|
sameHolder := doc.LeaseHolderType == req.HolderType && doc.LeaseHolderID == req.HolderID
|
||||||
if !sameHolder {
|
if !sameHolder && req.HolderType == LeaseHolderSquad && e.shouldReclaimSquadLease(ctx, doc) {
|
||||||
|
e.appendAuditLocked(doc, "lease_reclaim_orphan", 0, string(req.HolderType), RoleSystem, map[string]any{
|
||||||
|
"previous_holder_type": doc.LeaseHolderType,
|
||||||
|
"previous_holder_id": doc.LeaseHolderID,
|
||||||
|
})
|
||||||
|
doc.ClearLease()
|
||||||
|
e.persistLeaseLocked(ctx, doc)
|
||||||
|
} else if !sameHolder {
|
||||||
e.appendAuditLocked(doc, "lease_deny", 0, string(req.HolderType), RoleSystem, map[string]any{
|
e.appendAuditLocked(doc, "lease_deny", 0, string(req.HolderType), RoleSystem, map[string]any{
|
||||||
"requester_type": req.HolderType,
|
"requester_type": req.HolderType,
|
||||||
"requester_id": req.HolderID,
|
"requester_id": req.HolderID,
|
||||||
@@ -173,6 +180,26 @@ func (e *GovernanceEngine) HeartbeatLease(ctx context.Context, docID, holderType
|
|||||||
return doc, nil
|
return doc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// shouldReclaimSquadLease 当 squad 租约 holder 无对应 running task_run 时视为孤儿锁并回收。
|
||||||
|
func (e *GovernanceEngine) shouldReclaimSquadLease(ctx context.Context, doc *DocumentRecord) bool {
|
||||||
|
if doc == nil || doc.LeaseHolderType != LeaseHolderSquad {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
holderID := strings.TrimSpace(doc.LeaseHolderID)
|
||||||
|
if holderID == "" || !strings.HasPrefix(holderID, "run_") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if e.pool == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var status string
|
||||||
|
err := e.pool.QueryRow(ctx, `SELECT status FROM task_runs WHERE run_id = $1`, holderID).Scan(&status)
|
||||||
|
if err != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return status != "running"
|
||||||
|
}
|
||||||
|
|
||||||
// AssertWritableForComment 批注前检查:有效 edit lease 期间禁止批注
|
// AssertWritableForComment 批注前检查:有效 edit lease 期间禁止批注
|
||||||
func (e *GovernanceEngine) AssertWritableForComment(ctx context.Context, docID string) error {
|
func (e *GovernanceEngine) AssertWritableForComment(ctx context.Context, docID string) error {
|
||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
|
|||||||
Reference in New Issue
Block a user