refactor: 移除 Database registry 与 Instance 初始化依赖
Verify / test (pull_request) Successful in 11m8s
Verify / lint (pull_request) Successful in 19m34s
Verify / database-integration (pull_request) Successful in 21m51s

This commit is contained in:
2026-09-24 16:32:30 +00:00
parent 6db8a495fb
commit 23a2d81b50
16 changed files with 124 additions and 1199 deletions
@@ -22,11 +22,10 @@ import "errors"
type Phase string
const (
PhasePending Phase = "Pending"
PhaseValidating Phase = "Validating"
PhaseInitializingRegistry Phase = "InitializingRegistry"
PhaseReady Phase = "Ready"
PhaseDeleting Phase = "Deleting"
PhasePending Phase = "Pending"
PhaseValidating Phase = "Validating"
PhaseReady Phase = "Ready"
PhaseDeleting Phase = "Deleting"
)
type Readiness string
@@ -61,7 +60,7 @@ func Reconstitute(target ObservationTarget, snapshot Snapshot, deleting bool) (*
return nil, err
}
switch snapshot.Phase {
case PhasePending, PhaseValidating, PhaseInitializingRegistry, PhaseReady, PhaseDeleting:
case PhasePending, PhaseValidating, PhaseReady, PhaseDeleting:
default:
snapshot.Phase = PhasePending
snapshot.Readiness = Unknown
@@ -39,7 +39,7 @@ func lifecycleInstance(t *testing.T, snapshot instance.Snapshot, deleting bool)
// Acceptance: docs/database/domain-instance.md §3, checkpoint reconstruction and intent-only transitions.
func TestReconstituteCheckpoints(t *testing.T) {
for _, phase := range []instance.Phase{
instance.PhasePending, instance.PhaseValidating, instance.PhaseInitializingRegistry,
instance.PhasePending, instance.PhaseValidating,
instance.PhaseReady, instance.PhaseDeleting,
} {
snapshot := instance.Snapshot{Phase: phase, ObservedRevision: 1, Readiness: instance.Ready, ReportedVersion: "17"}
@@ -97,7 +97,7 @@ func TestDeletionRequiresRequestAndPreventsValidation(t *testing.T) {
t.Fatal("rejected deletion mutated state")
}
for _, phase := range []instance.Phase{
instance.PhasePending, instance.PhaseValidating, instance.PhaseInitializingRegistry,
instance.PhasePending, instance.PhaseValidating,
instance.PhaseReady, instance.PhaseDeleting,
} {
snapshot.Phase = phase
+15 -116
View File
@@ -27,8 +27,6 @@ const (
DependencyUnavailable
AuthenticationFailed
InsufficientPrivileges
RegistryIncompatible
RegistryNotUsable
)
// CheckResult 的零值表示未观察,不能视为成功。
@@ -70,32 +68,22 @@ func (c ManagementChecks) failure() Failure {
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
target ObservationTarget
version string
checks ManagementChecks
}
func NewCapabilityObservation(target ObservationTarget, version string,
checks ManagementChecks, registry RegistryState,
func NewCapabilityObservation(
target ObservationTarget,
version string,
checks ManagementChecks,
) (CapabilityObservation, error) {
if err := target.Validate(); err != nil {
return CapabilityObservation{}, err
}
return CapabilityObservation{target: target, version: version, checks: checks, registry: registry}, nil
return CapabilityObservation{target: target, version: version, checks: checks}, nil
}
func (o CapabilityObservation) managementFailure() Failure {
@@ -108,29 +96,6 @@ func (o CapabilityObservation) managementFailure() Failure {
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")
@@ -149,75 +114,11 @@ func (i *Instance) fail(failure Failure) {
i.snapshot.ObservedRevision = i.target.Revision().Value()
}
// AssessManagement 只推进意图,不执行 registry 写入,也不完成 observedRevision。
// AssessManagement 根据本轮完整能力观察完成验证,不执行外部写入。
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
}
@@ -227,12 +128,12 @@ func (i *Instance) assessComplete(o CapabilityObservation) {
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.snapshot = Snapshot{Phase: PhaseReady, ObservedRevision: i.target.Revision().Value(),
Readiness: Ready, ReportedVersion: o.version}
i.evidence = &o
}
@@ -244,10 +145,8 @@ func (i *Instance) AssessReadiness(o CapabilityObservation) error {
if i.snapshot.ObservedRevision != i.target.Revision().Value() {
return i.BeginValidation()
}
if o.managementFailure() != NoFailure || o.registry == RegistryUnavailable {
if o.managementFailure() != NoFailure {
i.snapshot.Phase = PhaseValidating
} else if o.registryFailure() != NoFailure {
i.snapshot.Phase = PhaseInitializingRegistry
}
i.assessComplete(o)
return nil
@@ -32,11 +32,9 @@ func completeChecks() instance.ManagementChecks {
}
}
func capability(t *testing.T, value *instance.Instance, checks instance.ManagementChecks,
registry instance.RegistryState,
) instance.CapabilityObservation {
func capability(t *testing.T, value *instance.Instance, checks instance.ManagementChecks) instance.CapabilityObservation {
t.Helper()
o, err := instance.NewCapabilityObservation(value.Target(), testServerVersion, checks, registry)
o, err := instance.NewCapabilityObservation(value.Target(), testServerVersion, checks)
if err != nil {
t.Fatal(err)
}
@@ -45,8 +43,8 @@ func capability(t *testing.T, value *instance.Instance, checks instance.Manageme
func readyInstance(t *testing.T) *instance.Instance {
t.Helper()
i := lifecycleInstance(t, instance.Snapshot{Phase: instance.PhaseInitializingRegistry}, false)
if err := i.AssessRegistryResult(instance.RegistryReadBack(capability(t, i, completeChecks(), instance.RegistryUsable))); err != nil {
i := lifecycleInstance(t, instance.Snapshot{Phase: instance.PhaseValidating}, false)
if err := i.AssessManagement(capability(t, i, completeChecks())); err != nil {
t.Fatal(err)
}
if err := i.RequireProvisioningReady(); err != nil {
@@ -60,38 +58,27 @@ func TestReadinessRequiresCompleteReadBack(t *testing.T) {
if err := i.BeginValidation(); err != nil {
t.Fatal(err)
}
absent := capability(t, i, completeChecks(), instance.RegistryAbsent)
if err := i.AssessManagement(absent); err != nil {
if i.RequireProvisioningReady() == nil {
t.Fatal("validation intent authorized provisioning")
}
if err := i.AssessManagement(capability(t, i, instance.ManagementChecks{})); err != nil {
t.Fatal(err)
}
if s := i.Snapshot(); s.Phase != instance.PhaseInitializingRegistry || s.ObservedRevision != 0 || s.Readiness != instance.Unknown {
t.Fatalf("management observation prematurely concluded readiness: %+v", s)
if snapshot := i.Snapshot(); snapshot.Phase != instance.PhaseValidating ||
snapshot.Readiness != instance.NotReady || snapshot.Failure != instance.ObservationIncomplete {
t.Fatalf("incomplete observation accepted: %+v", snapshot)
}
for range 2 {
decision, err := i.PlanRegistryPreparation(absent)
if err != nil || decision != instance.PreparationAllowed {
t.Fatalf("preparation: %v, %v", decision, err)
}
if i.RequireProvisioningReady() == nil {
t.Fatal("preparation authorized provisioning")
}
if i.RequireProvisioningReady() == nil {
t.Fatal("incomplete observation authorized provisioning")
}
if err := i.AssessRegistryResult(instance.RegistryReadBack(absent)); err != nil {
if err := i.AssessManagement(capability(t, i, completeChecks())); err != nil {
t.Fatal(err)
}
if i.Snapshot().Failure != instance.RegistryNotUsable || i.RequireProvisioningReady() == nil {
t.Fatal("absent registry accepted as ready")
}
usable := capability(t, i, completeChecks(), instance.RegistryUsable)
decision, err := i.PlanRegistryPreparation(usable)
if err != nil || decision != instance.AlreadyUsable {
t.Fatalf("retry after external preparation: %v, %v", decision, err)
}
if err := i.AssessRegistryResult(instance.RegistryReadBack(usable)); err != nil {
t.Fatal(err)
}
if s := i.Snapshot(); s.Readiness != instance.Ready || s.ReportedVersion != testServerVersion || s.ObservedRevision != i.Target().Revision().Value() {
t.Fatalf("complete observation not accepted: %+v", s)
snapshot := i.Snapshot()
if snapshot.Phase != instance.PhaseReady || snapshot.Readiness != instance.Ready ||
snapshot.ReportedVersion != testServerVersion ||
snapshot.ObservedRevision != i.Target().Revision().Value() {
t.Fatalf("complete management observation did not establish readiness: %+v", snapshot)
}
if err := i.RequireProvisioningReady(); err != nil {
t.Fatal(err)
@@ -109,7 +96,7 @@ func TestReadinessRecoveryAndInvalidation(t *testing.T) {
t.Fatal("persisted Ready fabricated fresh evidence")
}
for range 2 {
if err := restored.AssessReadiness(capability(t, restored, completeChecks(), instance.RegistryUsable)); err != nil {
if err := restored.AssessReadiness(capability(t, restored, completeChecks())); err != nil {
t.Fatal(err)
}
if err := restored.RequireProvisioningReady(); err != nil {
@@ -138,120 +125,77 @@ func TestReadinessRecoveryAndInvalidation(t *testing.T) {
}
func TestEachManagementCheckIsRequired(t *testing.T) {
for field := range 6 {
for _, result := range []instance.CheckResult{instance.CheckUnobserved, instance.CheckUnavailable,
instance.CheckAuthenticationFailed, instance.CheckInsufficientPrivileges, 255} {
checks := completeChecks()
fields := []*instance.CheckResult{&checks.Connection, &checks.Metadata, &checks.Roles,
&checks.Databases, &checks.Grants, &checks.Extensions}
*fields[field] = result
i := readyInstance(t)
if err := i.AssessReadiness(capability(t, i, checks, instance.RegistryUsable)); err != nil {
t.Fatal(err)
}
if s := i.Snapshot(); s.Phase != instance.PhaseValidating || s.Readiness != instance.NotReady ||
s.Failure == instance.NoFailure || i.RequireProvisioningReady() == nil {
t.Fatalf("check %d result %d accepted: %+v", field, result, s)
}
}
}
}
func TestRegistryDecisionsAndReadinessLoss(t *testing.T) {
for _, tc := range []struct {
state instance.RegistryState
decision instance.PreparationDecision
failure instance.Failure
checkNames := []string{"connection", "metadata", "roles", "databases", "grants", "extensions"}
failures := []struct {
name string
result instance.CheckResult
want instance.Failure
}{
{instance.RegistryUsable, instance.AlreadyUsable, instance.NoFailure},
{instance.RegistryAbsent, instance.PreparationAllowed, instance.RegistryNotUsable},
{instance.RegistryNeedsMigration, instance.PreparationAllowed, instance.RegistryNotUsable},
{instance.RegistryUnsupported, instance.PreparationDenied, instance.RegistryIncompatible},
{instance.RegistryUnavailable, instance.PreparationDenied, instance.DependencyUnavailable},
{instance.RegistryUnobserved, instance.PreparationDenied, instance.ObservationIncomplete},
{255, instance.PreparationDenied, instance.ObservationIncomplete},
} {
i := lifecycleInstance(t, instance.Snapshot{Phase: instance.PhaseInitializingRegistry}, false)
o := capability(t, i, completeChecks(), tc.state)
decision, err := i.PlanRegistryPreparation(o)
if err != nil || decision != tc.decision {
t.Fatalf("registry %d: %v, %v", tc.state, decision, err)
}
i = readyInstance(t)
if err := i.AssessReadiness(o); err != nil {
t.Fatal(err)
}
if i.Snapshot().Failure != tc.failure {
t.Fatalf("registry %d: %+v", tc.state, i.Snapshot())
}
if tc.state != instance.RegistryUsable {
wantPhase := instance.PhaseInitializingRegistry
if tc.state == instance.RegistryUnavailable {
wantPhase = instance.PhaseValidating
}
if i.Snapshot().Phase != wantPhase || i.RequireProvisioningReady() == nil {
t.Fatal("registry drift retained readiness")
{"unobserved", instance.CheckUnobserved, instance.ObservationIncomplete},
{"unavailable", instance.CheckUnavailable, instance.DependencyUnavailable},
{"authentication", instance.CheckAuthenticationFailed, instance.AuthenticationFailed},
{"privileges", instance.CheckInsufficientPrivileges, instance.InsufficientPrivileges},
{"unknown", 255, instance.ObservationIncomplete},
}
for field, name := range checkNames {
for _, failure := range failures {
for _, phase := range []instance.Phase{instance.PhaseValidating, instance.PhaseReady} {
t.Run(name+"/"+failure.name+"/"+string(phase), func(t *testing.T) {
checks := completeChecks()
fields := []*instance.CheckResult{
&checks.Connection, &checks.Metadata, &checks.Roles,
&checks.Databases, &checks.Grants, &checks.Extensions,
}
*fields[field] = failure.result
value := lifecycleInstance(t, instance.Snapshot{Phase: phase}, false)
assess := value.AssessManagement
if phase == instance.PhaseReady {
value = readyInstance(t)
assess = value.AssessReadiness
}
if err := assess(capability(t, value, checks)); err != nil {
t.Fatal(err)
}
snapshot := value.Snapshot()
if snapshot.Phase != instance.PhaseValidating ||
snapshot.Readiness != instance.NotReady ||
snapshot.Failure != failure.want ||
snapshot.ObservedRevision != value.Target().Revision().Value() {
t.Fatalf("incorrect failed observation: %+v", snapshot)
}
if value.RequireProvisioningReady() == nil {
t.Fatal("failed check authorized provisioning")
}
// 依赖恢复后重新验证,不保留失败或旧就绪证据。
if err := value.AssessManagement(capability(t, value, completeChecks())); err != nil {
t.Fatal(err)
}
if err := value.RequireProvisioningReady(); err != nil {
t.Fatal("dependency recovery did not restore readiness", err)
}
})
}
}
}
}
func TestInitializationRejectsIncompleteOrFailedManagement(t *testing.T) {
for _, tc := range []struct {
checks instance.ManagementChecks
registry instance.RegistryState
failure instance.Failure
}{
{instance.ManagementChecks{}, instance.RegistryUsable, instance.ObservationIncomplete},
{completeChecks(), instance.RegistryUnsupported, instance.RegistryIncompatible},
{completeChecks(), instance.RegistryUnavailable, instance.DependencyUnavailable},
} {
i := lifecycleInstance(t, instance.Snapshot{Phase: instance.PhaseValidating}, false)
if err := i.AssessManagement(capability(t, i, tc.checks, tc.registry)); err != nil {
t.Fatal(err)
}
if s := i.Snapshot(); s.Phase != instance.PhaseValidating || s.Failure != tc.failure ||
s.ObservedRevision != i.Target().Revision().Value() || s.Readiness != instance.NotReady {
t.Fatalf("invalid management accepted: %+v", s)
}
}
i := lifecycleInstance(t, instance.Snapshot{Phase: instance.PhaseInitializingRegistry}, false)
decision, err := i.PlanRegistryPreparation(capability(t, i, instance.ManagementChecks{}, instance.RegistryAbsent))
if err != nil || decision != instance.PreparationDenied || i.Snapshot().Failure != instance.ObservationIncomplete {
t.Fatal("incomplete management allowed registry writes")
}
if err := i.AssessRegistryResult(instance.RegistryReadBack(capability(t, i, completeChecks(), instance.RegistryUsable))); err != nil {
t.Fatal(err)
}
if err := i.RequireProvisioningReady(); err != nil {
t.Fatal("dependency recovery did not restore readiness", err)
}
}
func TestReadinessMethodsRejectWrongPhaseAndDeletion(t *testing.T) {
for _, deleting := range []bool{false, true} {
for _, phase := range []instance.Phase{instance.PhasePending, instance.PhaseValidating,
instance.PhaseInitializingRegistry, instance.PhaseReady, instance.PhaseDeleting} {
instance.PhaseReady, instance.PhaseDeleting} {
for _, operation := range []struct {
phase instance.Phase
apply func(*instance.Instance, instance.CapabilityObservation) error
}{
{instance.PhaseValidating, (*instance.Instance).AssessManagement},
{instance.PhaseReady, (*instance.Instance).AssessReadiness},
{instance.PhaseInitializingRegistry, func(i *instance.Instance, o instance.CapabilityObservation) error {
_, err := i.PlanRegistryPreparation(o)
return err
}},
{instance.PhaseInitializingRegistry, func(i *instance.Instance, o instance.CapabilityObservation) error {
return i.AssessRegistryResult(instance.RegistryReadBack(o))
}},
} {
if !deleting && operation.phase == phase {
continue
}
i := lifecycleInstance(t, instance.Snapshot{Phase: phase}, deleting)
before := i.Snapshot()
if err := operation.apply(i, capability(t, i, completeChecks(), instance.RegistryUsable)); err == nil {
if err := operation.apply(i, capability(t, i, completeChecks())); err == nil {
t.Fatalf("phase %s deleting=%t accepted operation for %s", phase, deleting, operation.phase)
}
if i.Snapshot() != before {
@@ -273,7 +217,7 @@ func TestOldGenerationObservationDoesNotReplaceEvidence(t *testing.T) {
if err != nil {
t.Fatal(err)
}
o, err := instance.NewCapabilityObservation(other, testServerVersion, completeChecks(), instance.RegistryUsable)
o, err := instance.NewCapabilityObservation(other, testServerVersion, completeChecks())
if err != nil {
t.Fatal(err)
}
@@ -286,44 +230,14 @@ func TestOldGenerationObservationDoesNotReplaceEvidence(t *testing.T) {
}
}
func TestPreparationFailureCannotEstablishReadiness(t *testing.T) {
i := lifecycleInstance(t, instance.Snapshot{Phase: instance.PhaseInitializingRegistry}, false)
for _, failure := range []instance.Failure{instance.DependencyUnavailable, instance.AuthenticationFailed,
instance.InsufficientPrivileges, instance.RegistryIncompatible} {
result, err := instance.RegistryPreparationFailed(i.Target(), failure)
if err != nil {
t.Fatal(err)
}
if err := i.AssessRegistryResult(result); err != nil {
t.Fatal(err)
}
if s := i.Snapshot(); s.Failure != failure || s.Readiness != instance.NotReady ||
s.Phase != instance.PhaseInitializingRegistry || i.RequireProvisioningReady() == nil {
t.Fatalf("failed operation accepted: %+v", s)
}
}
for _, failure := range []instance.Failure{instance.NoFailure, 255} {
if _, err := instance.RegistryPreparationFailed(i.Target(), failure); err == nil {
t.Fatal("invalid failure accepted")
}
}
}
func TestCapabilityInputsAndLifecycleGuards(t *testing.T) {
i := readyInstance(t)
if _, err := instance.NewCapabilityObservation(instance.ObservationTarget{}, testServerVersion,
completeChecks(), instance.RegistryUsable); err == nil {
completeChecks()); err == nil {
t.Fatal("invalid target accepted")
}
if _, err := instance.RegistryPreparationFailed(instance.ObservationTarget{}, instance.DependencyUnavailable); err == nil {
t.Fatal("invalid failure target accepted")
}
for _, method := range []func(instance.CapabilityObservation) error{
i.AssessManagement, i.AssessReadiness,
func(o instance.CapabilityObservation) error { _, err := i.PlanRegistryPreparation(o); return err },
func(o instance.CapabilityObservation) error {
return i.AssessRegistryResult(instance.RegistryReadBack(o))
},
} {
before := i.Snapshot()
if err := method(instance.CapabilityObservation{}); err == nil || i.Snapshot() != before {
@@ -333,13 +247,13 @@ func TestCapabilityInputsAndLifecycleGuards(t *testing.T) {
old := i.Snapshot()
old.ObservedRevision = 0
changed := lifecycleInstance(t, old, false)
if err := changed.AssessReadiness(capability(t, changed, completeChecks(), instance.RegistryUsable)); err != nil {
if err := changed.AssessReadiness(capability(t, changed, completeChecks())); err != nil {
t.Fatal(err)
}
if s := changed.Snapshot(); s.Phase != instance.PhaseValidating || s.ObservedRevision != 0 || s.Readiness != instance.Unknown {
t.Fatalf("changed generation accepted old checkpoint: %+v", s)
}
o, err := instance.NewCapabilityObservation(i.Target(), "", completeChecks(), instance.RegistryUsable)
o, err := instance.NewCapabilityObservation(i.Target(), "", completeChecks())
if err != nil {
t.Fatal(err)
}