Files
Agent-System/internal/docgov/lock_split.go
T

86 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package docgov
import (
"context"
"sync"
)
// docLockIndex 管理按 doc_id 的细粒度互斥锁。
// 全局锁只保护「分锁表」本身(短临界区);文档间操作互不阻塞。
// 锁序约定:docLocks.meta → docID(如需多文档,按 docID 字典序)。
type docLockIndex struct {
meta sync.Mutex
locks map[string]*sync.Mutex
}
func newDocLockIndex() *docLockIndex {
return &docLockIndex{locks: map[string]*sync.Mutex{}}
}
// lock 返回 docID 对应的互斥锁指针;调用方必须在 meta 锁内无其他副作用。
func (d *docLockIndex) lock(docID string) *sync.Mutex {
d.meta.Lock()
defer d.meta.Unlock()
mu, ok := d.locks[docID]
if !ok {
mu = &sync.Mutex{}
d.locks[docID] = mu
}
return mu
}
// docLockGuard 绑定一个 docID 的已获取互斥锁,确保按序释放。
type docLockGuard struct {
mu *sync.Mutex
release sync.Once
}
func (g *docLockGuard) Release() {
if g != nil && g.mu != nil {
g.release.Do(func() { g.mu.Unlock() })
}
}
func (e *GovernanceEngine) acquireDocLock(docID string) docLockGuard {
mu := e.docLocks.lock(docID)
mu.Lock()
return docLockGuard{mu: mu}
}
// withDocLock 以 docID 粒度执行 fn;fn 内的 DB I/O 仍持该文档锁,
// 但不再阻塞其他文档(原实现为全局 RWMutex)。
func (e *GovernanceEngine) withDocLock(docID string, fn func() error) error {
g := e.acquireDocLock(docID)
defer g.Release()
return fn()
}
// lookupMemDoc 返回内存中的文档记录(需在 doc 锁内调用)。
func (e *GovernanceEngine) lookupMemDoc(docID string) (*DocumentRecord, bool) {
doc, ok := e.memDocs[docID]
return doc, ok
}
// ensureDocLockedWithDB 在 doc 锁内做内存查找 + DB 兜底加载。
// 对应原 ensureDocumentLocked,但锁粒度为单文档。
func (e *GovernanceEngine) ensureDocLockedWithDB(ctx context.Context, docID string) (*DocumentRecord, error) {
if doc, ok := e.memDocs[docID]; ok {
return doc, nil
}
if e.pool != nil {
doc, err := e.loadDocumentFromDB(ctx, docID)
if err == nil && doc != nil {
return doc, nil
}
}
return nil, ErrDocumentNotFound
}
// loadDocumentFromDB 在无全局锁状态下从 PG 装载文档记录到内存。
// 这是 T20 B3 的核心:DB RTT 不再进入全局临界区。
func (e *GovernanceEngine) loadDocumentFromDB(ctx context.Context, docID string) (*DocumentRecord, error) {
e.mu.Lock()
defer e.mu.Unlock()
return e.loadDocumentFromDBLocked(ctx, docID)
}