Files
ayatori/internal/database/domain/instance/readiness.go
T
panxiao81 cd0d3a70ae
Verify / test (pull_request) Successful in 7m20s
Verify / lint (pull_request) Successful in 7m51s
feat: 实现 Instance Ready 领域判定与恢复规则
2026-09-21 07:49:41 +00:00

264 lines
7.5 KiB
Go

/*
Copyright 2026.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package instance
import "errors"
// Failure 只表示安全类别;驱动错误、凭据和 Condition 文案留在应用边界。
type Failure uint8
const (
NoFailure Failure = iota
ObservationIncomplete
DependencyUnavailable
AuthenticationFailed
InsufficientPrivileges
RegistryIncompatible
RegistryNotUsable
)
// CheckResult 的零值表示未观察,不能视为成功。
type CheckResult uint8
const (
CheckUnobserved CheckResult = iota
CheckPassed
CheckUnavailable
CheckAuthenticationFailed
CheckInsufficientPrivileges
)
// ManagementChecks 分别记录所需能力;SQL 探测和同轮次关联由 adapter/application 保证。
// Extensions 不代表任意扩展均可安装;具体请求仍需支持检查、执行及回读。
type ManagementChecks struct {
Connection CheckResult
Metadata CheckResult
Roles CheckResult
Databases CheckResult
Grants CheckResult
Extensions CheckResult
}
func (c ManagementChecks) failure() Failure {
for _, check := range []CheckResult{c.Connection, c.Metadata, c.Roles, c.Databases, c.Grants, c.Extensions} {
switch check {
case CheckPassed:
case CheckUnavailable:
return DependencyUnavailable
case CheckAuthenticationFailed:
return AuthenticationFailed
case CheckInsufficientPrivileges:
return InsufficientPrivileges
default:
return ObservationIncomplete
}
}
return NoFailure
}
type RegistryState uint8
const (
RegistryUnobserved RegistryState = iota
RegistryAbsent
RegistryNeedsMigration
RegistryUsable
RegistryUnsupported
RegistryUnavailable
)
// CapabilityObservation 是值对象,不包含连接、凭据或可变集合。
type CapabilityObservation struct {
target ObservationTarget
version string
checks ManagementChecks
registry RegistryState
}
func NewCapabilityObservation(target ObservationTarget, version string,
checks ManagementChecks, registry RegistryState,
) (CapabilityObservation, error) {
if err := target.Validate(); err != nil {
return CapabilityObservation{}, err
}
return CapabilityObservation{target: target, version: version, checks: checks, registry: registry}, nil
}
func (o CapabilityObservation) managementFailure() Failure {
if failure := o.checks.failure(); failure != NoFailure {
return failure
}
if o.version == "" {
return ObservationIncomplete
}
return NoFailure
}
func (o CapabilityObservation) registryFailure() Failure {
switch o.registry {
case RegistryUsable:
return NoFailure
case RegistryAbsent, RegistryNeedsMigration:
return RegistryNotUsable
case RegistryUnsupported:
return RegistryIncompatible
case RegistryUnavailable:
return DependencyUnavailable
default:
return ObservationIncomplete
}
}
type PreparationDecision uint8
const (
PreparationDenied PreparationDecision = iota
PreparationAllowed
AlreadyUsable
)
func (i *Instance) acceptObservation(o CapabilityObservation, phase Phase) error {
if !i.target.Matches(o.target) {
return errors.New("capability observation target does not match instance")
}
if i.deleting || i.snapshot.Phase != phase {
return errors.New("capability observation is not allowed in current lifecycle")
}
return nil
}
func (i *Instance) fail(failure Failure) {
i.evidence = nil
i.extensions = ExtensionSupport{}
i.snapshot.Readiness = NotReady
i.snapshot.Failure = failure
i.snapshot.ObservedRevision = i.target.Revision().Value()
}
// AssessManagement 只推进意图,不执行 registry 写入,也不完成 observedRevision。
func (i *Instance) AssessManagement(o CapabilityObservation) error {
if err := i.acceptObservation(o, PhaseValidating); err != nil {
return err
}
if failure := o.managementFailure(); failure != NoFailure {
i.fail(failure)
return nil
}
if failure := o.registryFailure(); failure != NoFailure && failure != RegistryNotUsable {
i.fail(failure)
return nil
}
i.snapshot.Phase = PhaseInitializingRegistry
i.snapshot.Readiness = Unknown
i.snapshot.Failure = NoFailure
i.evidence = nil
return nil
}
// PlanRegistryPreparation 不证明 checkpoint 已落盘;应用层必须先保存意图再执行写入。
func (i *Instance) PlanRegistryPreparation(o CapabilityObservation) (PreparationDecision, error) {
if err := i.acceptObservation(o, PhaseInitializingRegistry); err != nil {
return PreparationDenied, err
}
if failure := o.managementFailure(); failure != NoFailure {
i.fail(failure)
return PreparationDenied, nil
}
switch o.registry {
case RegistryUsable:
return AlreadyUsable, nil
case RegistryAbsent, RegistryNeedsMigration:
return PreparationAllowed, nil
default:
i.fail(o.registryFailure())
return PreparationDenied, nil
}
}
// RegistryPreparationResult 只能是安全失败或完整回读,不能表达裸操作成功。
type RegistryPreparationResult struct {
observation CapabilityObservation
failure Failure
}
func RegistryReadBack(o CapabilityObservation) RegistryPreparationResult {
return RegistryPreparationResult{observation: o}
}
func RegistryPreparationFailed(target ObservationTarget, failure Failure) (RegistryPreparationResult, error) {
if err := target.Validate(); err != nil {
return RegistryPreparationResult{}, err
}
if failure < ObservationIncomplete || failure > RegistryNotUsable {
return RegistryPreparationResult{}, errors.New("registry preparation requires a known failure category")
}
return RegistryPreparationResult{observation: CapabilityObservation{target: target}, failure: failure}, nil
}
func (i *Instance) AssessRegistryResult(result RegistryPreparationResult) error {
o := result.observation
if err := i.acceptObservation(o, PhaseInitializingRegistry); err != nil {
return err
}
if result.failure != NoFailure {
i.fail(result.failure)
return nil
}
i.assessComplete(o)
return nil
}
func (i *Instance) assessComplete(o CapabilityObservation) {
if failure := o.managementFailure(); failure != NoFailure {
i.fail(failure)
return
}
if failure := o.registryFailure(); failure != NoFailure {
i.fail(failure)
return
}
i.snapshot = Snapshot{Phase: PhaseReady, ObservedRevision: i.target.Revision().Value(),
Readiness: Ready, ReportedVersion: o.version}
i.evidence = &o
}
// AssessReadiness 每轮接收完整事实,失败立即撤销本轮供应能力。
func (i *Instance) AssessReadiness(o CapabilityObservation) error {
if err := i.acceptObservation(o, PhaseReady); err != nil {
return err
}
if i.snapshot.ObservedRevision != i.target.Revision().Value() {
return i.BeginValidation()
}
if o.managementFailure() != NoFailure || o.registry == RegistryUnavailable {
i.snapshot.Phase = PhaseValidating
} else if o.registryFailure() != NoFailure {
i.snapshot.Phase = PhaseInitializingRegistry
}
i.assessComplete(o)
return nil
}
// RequireProvisioningReady 仅检查 Instance 前置条件,不授予 Tenant 所有权或外部写入许可。
func (i *Instance) RequireProvisioningReady() error {
if i.deleting || i.evidence == nil || i.snapshot.Phase != PhaseReady || i.snapshot.Readiness != Ready ||
i.snapshot.ObservedRevision != i.target.Revision().Value() {
return errors.New("instance is not ready for provisioning")
}
return nil
}