fix(cluster): 修复身份事务阻塞与 Worker 容量抖动

将身份协调和安全事件心跳改为 PostgreSQL 单 Leader 执行,并从独立 Worker 进程中移除身份运行时,避免多副本重复写同一状态。

为安全事件事务增加锁等待、空闲事务超时及独立回滚上下文;Worker 需连续丢失六次心跳后才判定失效,降低跨节点抖动导致的容量反复扩缩。

验证:Go 全量测试、go vet、Race 聚焦测试、PostgreSQL 18 Leader/安全事件/Worker 分配集成测试及迁移测试通过。
This commit is contained in:
2026-07-29 23:04:41 +08:00
parent 3886048e0f
commit 91451b3c86
10 changed files with 497 additions and 76 deletions
+46 -10
View File
@@ -10,6 +10,42 @@ import (
"github.com/jackc/pgx/v5"
)
const (
securityEventTransactionLockTimeout = 5 * time.Second
securityEventTransactionIdleTimeout = 15 * time.Second
transactionRollbackTimeout = 5 * time.Second
)
func (s *Store) beginSecurityEventTransaction(ctx context.Context) (pgx.Tx, error) {
tx, err := s.pool.Begin(ctx)
if err != nil {
return nil, err
}
settings := []struct {
name string
value string
}{
{name: "idle_in_transaction_session_timeout", value: securityEventTransactionIdleTimeout.String()},
{name: "lock_timeout", value: securityEventTransactionLockTimeout.String()},
}
for _, setting := range settings {
if _, err := tx.Exec(ctx, `SELECT set_config($1, $2, true)`, setting.name, setting.value); err != nil {
rollbackSecurityEventTransaction(tx)
return nil, err
}
}
return tx, nil
}
func rollbackSecurityEventTransaction(tx pgx.Tx) {
if tx == nil {
return
}
ctx, cancel := context.WithTimeout(context.Background(), transactionRollbackTimeout)
defer cancel()
_ = tx.Rollback(ctx)
}
type ApplySessionRevokedInput struct {
Issuer string
Audience string
@@ -41,11 +77,11 @@ func (s *Store) ApplySessionRevoked(ctx context.Context, input ApplySessionRevok
if input.SubjectType == "" {
input.SubjectType = "principal"
}
tx, err := s.pool.Begin(ctx)
tx, err := s.beginSecurityEventTransaction(ctx)
if err != nil {
return ApplySecurityEventResult{}, err
}
defer func() { _ = tx.Rollback(ctx) }()
defer rollbackSecurityEventTransaction(tx)
subjectHash := shortSecurityEventHash(input.Subject)
tag, err := tx.Exec(ctx, `
INSERT INTO gateway_security_event_receipts (
@@ -146,11 +182,11 @@ WHERE session.gateway_user_id = gateway_user.id
}
func (s *Store) EnsureSecurityEventStreamState(ctx context.Context, issuer, audience, streamID string) error {
tx, err := s.pool.Begin(ctx)
tx, err := s.beginSecurityEventTransaction(ctx)
if err != nil {
return err
}
defer func() { _ = tx.Rollback(ctx) }()
defer rollbackSecurityEventTransaction(tx)
if _, err := tx.Exec(ctx, `
INSERT INTO gateway_security_event_stream_state(issuer,audience,stream_id,mode)
VALUES($1,$2,$3::uuid,'bootstrap')
@@ -184,11 +220,11 @@ func (s *Store) BeginSecurityEventVerification(ctx context.Context, issuer, audi
if len(stateHash) != sha256.Size {
return errors.New("verification state hash must be SHA-256")
}
tx, err := s.pool.Begin(ctx)
tx, err := s.beginSecurityEventTransaction(ctx)
if err != nil {
return err
}
defer func() { _ = tx.Rollback(ctx) }()
defer rollbackSecurityEventTransaction(tx)
tag, err := tx.Exec(ctx, `
UPDATE gateway_security_event_stream_state
SET pending_state_hash=$3,pending_state_created_at=$4,updated_at=now()
@@ -212,11 +248,11 @@ ON CONFLICT(issuer,audience,state_hash) DO NOTHING`, issuer, audience, stateHash
}
func (s *Store) ConfirmSecurityEventVerification(ctx context.Context, issuer, audience, streamID, jti string, stateHash []byte, now time.Time) (bool, error) {
tx, err := s.pool.Begin(ctx)
tx, err := s.beginSecurityEventTransaction(ctx)
if err != nil {
return false, err
}
defer func() { _ = tx.Rollback(ctx) }()
defer rollbackSecurityEventTransaction(tx)
tag, err := tx.Exec(ctx, `
INSERT INTO gateway_security_event_receipts(issuer,jti,audience,event_type,subject_hash)
VALUES($1,$2::uuid,$3,$4,$5)
@@ -294,11 +330,11 @@ ON CONFLICT(issuer,jti) DO NOTHING`, issuer, jti, audience, eventType, shortSecu
// status change and immediately moves OIDC traffic to introspection. Push is
// trusted again only after the normal consecutive-verification recovery path.
func (s *Store) ApplySecurityEventStreamUpdated(ctx context.Context, issuer, audience, jti, streamID, status, reason string, now time.Time) (bool, error) {
tx, err := s.pool.Begin(ctx)
tx, err := s.beginSecurityEventTransaction(ctx)
if err != nil {
return false, err
}
defer func() { _ = tx.Rollback(ctx) }()
defer rollbackSecurityEventTransaction(tx)
tag, err := tx.Exec(ctx, `
INSERT INTO gateway_security_event_receipts(issuer,jti,audience,event_type,subject_hash)
VALUES($1,$2::uuid,$3,$4,$5)