Files
Agent-System-Backend/internal/store/dialect.go
T
weichen f84733d128
ci / vet-and-test (pull_request) Successful in 43s
Add shared execution budgets and the action-authz matrix (U3 slice 2)
Why: plan U3 remainder (R8/KTD13) - execution admission must be bounded and shared (leases in the database, parents yield while waiting so budgets cannot deadlock parent-child), and project authorization must distinguish read/edit/approve/execute/admin with default-deny for unconfigured actions.

Evidence: full suite green on SQLite and against real PostgreSQL 18.6 + Redis (isolated instance); new tests - admission_pg_test (enforcement, per-user, concurrent no-oversell), runmgr admission tests (saturated-budget sync spawn progresses, background children share the budget, bounded queue 429), server action-matrix tests (default deny, legacy adapter read-only, action-aware authoritative + fail-closed).

Impact: admission_leases (v21) with atomic lease accounting under an advisory lock; runtime.execBudgetGlobal/PerUser/execQueueMax settings (all zero = off, historical behavior preserved); conversation turns, background and sync subagents share the pool, parents yield around synchronous children and HITL waits; queue-full rejects 429 before writing; server gains ProjectAction matrix + SetProjectActionAuthorizer with legacy EXT-8 read fallback.

Refs: proposals/2026-10-11-collaboration-storage-and-distribution.md U3 R8/KTD13; docs/guides/team-storage.md section 9.
2026-10-11 05:18:12 +00:00

66 lines
2.0 KiB
Go

package store
import (
"database/sql"
"fmt"
"strings"
)
// dialect 收敛 SQLite / PostgreSQL 差异。全部查询以 ? 占位符书写,PG 执行前 rebind。
type dialect interface {
name() string
rebind(query string) string
// lockConversation 在事务内锁定会话的事件序列分配(跨实例安全)。
// PG:事务级咨询锁;SQLite:单写连接天然串行,无需额外锁。
lockConversation(tx *sql.Tx, conversationID string) error
// lockScope 锁定设置 scope 的读改写(owner+scope)。
lockScope(tx *sql.Tx, owner, scope string) error
// lockAdmission 锁定执行许可账本的清点与登记(U3 admission)。
lockAdmission(tx *sql.Tx) error
}
// ---- SQLite(嵌入式档)----
type sqliteDialect struct{}
func (sqliteDialect) name() string { return "sqlite" }
func (sqliteDialect) rebind(q string) string { return q }
func (sqliteDialect) lockConversation(*sql.Tx, string) error { return nil }
func (sqliteDialect) lockScope(*sql.Tx, string, string) error { return nil }
func (sqliteDialect) lockAdmission(*sql.Tx) error { return nil }
// ---- PostgreSQL(多用户/多实例档)----
type postgresDialect struct{}
func (postgresDialect) name() string { return "postgres" }
func (postgresDialect) rebind(q string) string {
var b strings.Builder
n := 0
for i := 0; i < len(q); i++ {
if q[i] == '?' {
n++
fmt.Fprintf(&b, "$%d", n)
continue
}
b.WriteByte(q[i])
}
return b.String()
}
func (postgresDialect) lockConversation(tx *sql.Tx, conversationID string) error {
_, err := tx.Exec(`SELECT pg_advisory_xact_lock(hashtext($1))`, "conv:"+conversationID)
return err
}
func (postgresDialect) lockScope(tx *sql.Tx, owner, scope string) error {
_, err := tx.Exec(`SELECT pg_advisory_xact_lock(hashtext($1))`, "scope:"+owner+"/"+scope)
return err
}
func (postgresDialect) lockAdmission(tx *sql.Tx) error {
_, err := tx.Exec(`SELECT pg_advisory_xact_lock(hashtext($1))`, "admission:exec-slots")
return err
}