补充数据库过期清理并发保护
新增 expiredCleaning 状态,避免单项过期清理与批量清理重复处理同一临时凭据;支持批量接管失败的过期清理,补充专项回归测试,并同步更新四份进度清单。
This commit is contained in:
parent
1620a83461
commit
0334e99406
@ -75,24 +75,26 @@ type DatabaseVerificationSession struct {
|
||||
}
|
||||
|
||||
type DatabaseVerificationManager struct {
|
||||
mu sync.Mutex
|
||||
cleanupMu sync.Mutex
|
||||
base config.Config
|
||||
store DatabaseSecretStore
|
||||
verify func(context.Context, config.DatabaseConfig) error
|
||||
now func() time.Time
|
||||
sessions map[string]databaseVerificationSession
|
||||
expiredSession map[string]expiredDatabaseVerificationSession
|
||||
pendingCleanup map[string]struct{}
|
||||
inFlight int
|
||||
mu sync.Mutex
|
||||
cleanupMu sync.Mutex
|
||||
base config.Config
|
||||
store DatabaseSecretStore
|
||||
verify func(context.Context, config.DatabaseConfig) error
|
||||
now func() time.Time
|
||||
sessions map[string]databaseVerificationSession
|
||||
expiredSession map[string]expiredDatabaseVerificationSession
|
||||
expiredCleaning map[string]struct{}
|
||||
pendingCleanup map[string]struct{}
|
||||
inFlight int
|
||||
}
|
||||
|
||||
func NewDatabaseVerificationManager(base config.Config, store DatabaseSecretStore) *DatabaseVerificationManager {
|
||||
return &DatabaseVerificationManager{
|
||||
base: base, store: store, verify: database.VerifyWritable, now: time.Now,
|
||||
sessions: make(map[string]databaseVerificationSession),
|
||||
expiredSession: make(map[string]expiredDatabaseVerificationSession),
|
||||
pendingCleanup: make(map[string]struct{}),
|
||||
sessions: make(map[string]databaseVerificationSession),
|
||||
expiredSession: make(map[string]expiredDatabaseVerificationSession),
|
||||
expiredCleaning: make(map[string]struct{}),
|
||||
pendingCleanup: make(map[string]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,6 +226,7 @@ func (m *DatabaseVerificationManager) Discard(token string) error {
|
||||
if ok {
|
||||
delete(m.sessions, token)
|
||||
m.expiredSession[token] = entry
|
||||
m.expiredCleaning[token] = struct{}{}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
m.deleteExpiredSessions(expired)
|
||||
@ -317,6 +320,20 @@ func (m *DatabaseVerificationManager) DiscardAllExcept(protectedReferences ...st
|
||||
}
|
||||
entry := expiredDatabaseVerificationSession{Token: token, Session: session}
|
||||
m.expiredSession[token] = entry
|
||||
m.expiredCleaning[token] = struct{}{}
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
for token, entry := range m.expiredSession {
|
||||
if protected[strings.TrimSpace(entry.Session.CredentialRef)] {
|
||||
if _, cleaning := m.expiredCleaning[token]; !cleaning {
|
||||
delete(m.expiredSession, token)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if _, cleaning := m.expiredCleaning[token]; cleaning {
|
||||
continue
|
||||
}
|
||||
m.expiredCleaning[token] = struct{}{}
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
@ -370,6 +387,7 @@ func (m *DatabaseVerificationManager) clearExpiredLocked(now time.Time) []expire
|
||||
entry := expiredDatabaseVerificationSession{Token: token, Session: session}
|
||||
delete(m.sessions, token)
|
||||
m.expiredSession[token] = entry
|
||||
m.expiredCleaning[token] = struct{}{}
|
||||
expired = append(expired, entry)
|
||||
}
|
||||
return expired
|
||||
@ -441,6 +459,7 @@ func (m *DatabaseVerificationManager) restoreExpiredSession(entry expiredDatabas
|
||||
m.mu.Lock()
|
||||
if current, exists := m.expiredSession[entry.Token]; exists && current.Session.CredentialRef == entry.Session.CredentialRef {
|
||||
delete(m.expiredSession, entry.Token)
|
||||
delete(m.expiredCleaning, entry.Token)
|
||||
if _, active := m.sessions[entry.Token]; !active {
|
||||
m.sessions[entry.Token] = entry.Session
|
||||
}
|
||||
@ -455,6 +474,7 @@ func (m *DatabaseVerificationManager) removeExpiredSession(entry expiredDatabase
|
||||
m.mu.Lock()
|
||||
if current, exists := m.expiredSession[entry.Token]; exists && current.Session.CredentialRef == entry.Session.CredentialRef {
|
||||
delete(m.expiredSession, entry.Token)
|
||||
delete(m.expiredCleaning, entry.Token)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
@ -230,6 +230,40 @@ func TestDatabaseBatchDoesNotTakeOverConcurrentExpiryCleanup(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDatabaseBatchTakesOverFailedExpiryCleanup(t *testing.T) {
|
||||
store := &lifecycleDatabaseStore{}
|
||||
manager := NewDatabaseVerificationManager(config.Default(t.TempDir()), store)
|
||||
manager.verify = func(context.Context, config.DatabaseConfig) error { return nil }
|
||||
result, err := manager.Verify(t.Context(), databaseLifecycleInput())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
manager.mu.Lock()
|
||||
entry := expiredDatabaseVerificationSession{
|
||||
Token: result.VerificationToken,
|
||||
Session: manager.sessions[result.VerificationToken],
|
||||
}
|
||||
delete(manager.sessions, result.VerificationToken)
|
||||
manager.expiredSession[result.VerificationToken] = entry
|
||||
manager.mu.Unlock()
|
||||
|
||||
manager.DiscardAll()
|
||||
|
||||
if store.deleteCalls.Load() != 1 {
|
||||
t.Fatalf("expected one cleanup attempt, got %d", store.deleteCalls.Load())
|
||||
}
|
||||
if len(manager.expiredSession) != 0 || len(manager.expiredCleaning) != 0 {
|
||||
t.Fatal("batch cleanup left expired state behind")
|
||||
}
|
||||
store.mu.Lock()
|
||||
_, retained := store.values[result.CredentialRef]
|
||||
store.mu.Unlock()
|
||||
if retained {
|
||||
t.Fatal("batch cleanup did not delete the credential")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDatabasePendingCleanupCountsTowardCapacity(t *testing.T) {
|
||||
store := &lifecycleDatabaseStore{beforeDelete: func(context.Context) error { return errors.New("测试删除失败") }}
|
||||
manager := NewDatabaseVerificationManager(config.Default(t.TempDir()), store)
|
||||
|
||||
@ -3801,3 +3801,15 @@ Linux Ubuntu 22.04 上的 `Scert-admin` 手动启停、无开机自启动、无
|
||||
- [~] 进行中:P0-10 初始化回滚、任务链、重试幂等、三数据库实机矩阵和统一复审仍需继续。
|
||||
- [ ] 未开始:P0-12、真实生产数据库清理与初始化、密码 SSH、远端多服务器部署、真实 HTTPS/DNS/ACME、Linux/Windows 服务和最终生产门禁。
|
||||
- [!] 不得标记 `[✓]`;总体验收仍为 **P0-03:完成,待复审**。
|
||||
|
||||
|
||||
## 本轮更新(2026-09-30,expiredCleaning 状态保护)
|
||||
|
||||
总体验收位置仍为 **P0-03:完成,待复审**。本轮完成数据库过期清理的执行状态和批量接管边界,仅作为本地开发和回归验证证据,不等同于生产验收通过。
|
||||
|
||||
- [x] 过期会话开始清理时记录 `expiredCleaning`,避免单项清理与批量清理重复删除。
|
||||
- [x] 批量清理可接管失败后尚未执行的过期清理,但不接管正在执行的清理。
|
||||
- [x] 新增批量接管失败过期清理的回归测试,专项测试和竞态测试均通过。
|
||||
- [~] P0-10 其他任务、跨数据库实机矩阵和统一复审继续进行中。
|
||||
- [ ] 真实生产数据库、密码 SSH、远端部署、HTTPS/DNS、ACME、Linux/Windows 服务和最终生产门禁仍未完成。
|
||||
- [!] 不新增 `[✓]`,不以本地结果代替生产验收。
|
||||
|
||||
@ -1106,3 +1106,15 @@ P0-05 从 `[!]` 调整为 `[~]`,并未标记完成;P0-04 剩余门禁和全
|
||||
- [~] 进行中:P0-10 初始化回滚、任务链、重试幂等、三数据库实机矩阵和统一复审仍需继续。
|
||||
- [ ] 未开始:P0-12、真实生产数据库清理与初始化、密码 SSH、远端多服务器部署、真实 HTTPS/DNS/ACME、Linux/Windows 服务和最终生产门禁。
|
||||
- [!] 不得标记 `[✓]`;总体验收仍为 **P0-03:完成,待复审**。
|
||||
|
||||
|
||||
## 本轮更新(2026-09-30,expiredCleaning 状态保护)
|
||||
|
||||
总体验收位置仍为 **P0-03:完成,待复审**。本轮完成数据库过期清理的执行状态和批量接管边界,仅作为本地开发和回归验证证据,不等同于生产验收通过。
|
||||
|
||||
- [x] 过期会话开始清理时记录 `expiredCleaning`,避免单项清理与批量清理重复删除。
|
||||
- [x] 批量清理可接管失败后尚未执行的过期清理,但不接管正在执行的清理。
|
||||
- [x] 新增批量接管失败过期清理的回归测试,专项测试和竞态测试均通过。
|
||||
- [~] P0-10 其他任务、跨数据库实机矩阵和统一复审继续进行中。
|
||||
- [ ] 真实生产数据库、密码 SSH、远端部署、HTTPS/DNS、ACME、Linux/Windows 服务和最终生产门禁仍未完成。
|
||||
- [!] 不新增 `[✓]`,不以本地结果代替生产验收。
|
||||
|
||||
@ -1755,3 +1755,15 @@ P0-11 生产验收门禁
|
||||
- [~] 进行中:P0-10 初始化回滚、任务链、重试幂等、三数据库实机矩阵和统一复审仍需继续。
|
||||
- [ ] 未开始:P0-12、真实生产数据库清理与初始化、密码 SSH、远端多服务器部署、真实 HTTPS/DNS/ACME、Linux/Windows 服务和最终生产门禁。
|
||||
- [!] 不得标记 `[✓]`;总体验收仍为 **P0-03:完成,待复审**。
|
||||
|
||||
|
||||
## 本轮更新(2026-09-30,expiredCleaning 状态保护)
|
||||
|
||||
总体验收位置仍为 **P0-03:完成,待复审**。本轮完成数据库过期清理的执行状态和批量接管边界,仅作为本地开发和回归验证证据,不等同于生产验收通过。
|
||||
|
||||
- [x] 过期会话开始清理时记录 `expiredCleaning`,避免单项清理与批量清理重复删除。
|
||||
- [x] 批量清理可接管失败后尚未执行的过期清理,但不接管正在执行的清理。
|
||||
- [x] 新增批量接管失败过期清理的回归测试,专项测试和竞态测试均通过。
|
||||
- [~] P0-10 其他任务、跨数据库实机矩阵和统一复审继续进行中。
|
||||
- [ ] 真实生产数据库、密码 SSH、远端部署、HTTPS/DNS、ACME、Linux/Windows 服务和最终生产门禁仍未完成。
|
||||
- [!] 不新增 `[✓]`,不以本地结果代替生产验收。
|
||||
|
||||
@ -679,3 +679,15 @@ P0-06 与 P0-07 均维持 `[~]`,六阶段中的隔离、生产和最终验收
|
||||
- [~] 进行中:P0-10 初始化回滚、任务链、重试幂等、三数据库实机矩阵和统一复审仍需继续。
|
||||
- [ ] 未开始:P0-12、真实生产数据库清理与初始化、密码 SSH、远端多服务器部署、真实 HTTPS/DNS/ACME、Linux/Windows 服务和最终生产门禁。
|
||||
- [!] 不得标记 `[✓]`;总体验收仍为 **P0-03:完成,待复审**。
|
||||
|
||||
|
||||
## 本轮更新(2026-09-30,expiredCleaning 状态保护)
|
||||
|
||||
总体验收位置仍为 **P0-03:完成,待复审**。本轮完成数据库过期清理的执行状态和批量接管边界,仅作为本地开发和回归验证证据,不等同于生产验收通过。
|
||||
|
||||
- [x] 过期会话开始清理时记录 `expiredCleaning`,避免单项清理与批量清理重复删除。
|
||||
- [x] 批量清理可接管失败后尚未执行的过期清理,但不接管正在执行的清理。
|
||||
- [x] 新增批量接管失败过期清理的回归测试,专项测试和竞态测试均通过。
|
||||
- [~] P0-10 其他任务、跨数据库实机矩阵和统一复审继续进行中。
|
||||
- [ ] 真实生产数据库、密码 SSH、远端部署、HTTPS/DNS、ACME、Linux/Windows 服务和最终生产门禁仍未完成。
|
||||
- [!] 不新增 `[✓]`,不以本地结果代替生产验收。
|
||||
|
||||
Loading…
Reference in New Issue
Block a user