Cache approved dynamic descriptors per operation
This commit is contained in:
@@ -461,6 +461,13 @@ Focused check: `go test ./internal/engine -run 'History|Explain|Inheritance|Over
|
|||||||
Finding: **A05**; requires Stage 2.
|
Finding: **A05**; requires Stage 2.
|
||||||
Owners: `internal/engine/value_operation.go`, projection/clone tests.
|
Owners: `internal/engine/value_operation.go`, projection/clone tests.
|
||||||
|
|
||||||
|
Status: **complete** (A05). Prior completed commits: `2736ddf`, `deb9aa5`,
|
||||||
|
`ae3408a`, `ea27f17`, `08a8c25`, `877c497`, `8aba43d`, `7bf2f0c`,
|
||||||
|
`0f9ecf1`, `119b42c`, `247a12b`, `4fcff5f`, `12d350a`. Validation passed:
|
||||||
|
`env GOTOOLCHAIN=local GOPROXY=off GOTELEMETRY=off go test ./internal/engine -run 'Dynamic|Projection|Clone|Resolution' -count=1`,
|
||||||
|
`env GOTOOLCHAIN=local GOPROXY=off GOTELEMETRY=off go test ./internal/engine -count=1`,
|
||||||
|
and `git diff --check`.
|
||||||
|
|
||||||
Add an operation-local set of successfully approved concrete types/descriptors.
|
Add an operation-local set of successfully approved concrete types/descriptors.
|
||||||
After a completed descriptor build and a full successful secret-eligibility walk,
|
After a completed descriptor build and a full successful secret-eligibility walk,
|
||||||
record approval. Subsequent values of that type in the same `valueOperation` reuse
|
record approval. Subsequent values of that type in the same `valueOperation` reuse
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
||||||
)
|
)
|
||||||
|
|
||||||
type unsupportedDynamicRoot struct {
|
type unsupportedDynamicRoot struct {
|
||||||
@@ -63,6 +65,9 @@ func TestDynamicDescriptorFailuresRollBackProvisionalTypes(t *testing.T) {
|
|||||||
if _, issue := operation.dynamicDescriptor(reflect.TypeFor[unsupportedDynamicRoot](), Keys("failure")); issue == nil || issue.code != CodeSchemaDynamicType {
|
if _, issue := operation.dynamicDescriptor(reflect.TypeFor[unsupportedDynamicRoot](), Keys("failure")); issue == nil || issue.code != CodeSchemaDynamicType {
|
||||||
t.Fatalf("unsupported dynamic descriptor issue = %#v", issue)
|
t.Fatalf("unsupported dynamic descriptor issue = %#v", issue)
|
||||||
}
|
}
|
||||||
|
if _, approved := operation.approvedDynamic[reflect.TypeFor[unsupportedDynamicRoot]()]; approved {
|
||||||
|
t.Fatal("unsupported dynamic descriptor was cached as eligible")
|
||||||
|
}
|
||||||
if schema.types[reflect.TypeFor[map[string]string]()] != prior {
|
if schema.types[reflect.TypeFor[map[string]string]()] != prior {
|
||||||
t.Fatal("failed build removed a previously completed descriptor")
|
t.Fatal("failed build removed a previously completed descriptor")
|
||||||
}
|
}
|
||||||
@@ -87,9 +92,69 @@ func TestDynamicDescriptorFailuresRollBackProvisionalTypes(t *testing.T) {
|
|||||||
if _, issue := secretOperation.dynamicDescriptor(reflect.TypeFor[secretDynamicRoot](), Keys("secret")); issue == nil || issue.code != CodeSchemaDynamicType {
|
if _, issue := secretOperation.dynamicDescriptor(reflect.TypeFor[secretDynamicRoot](), Keys("secret")); issue == nil || issue.code != CodeSchemaDynamicType {
|
||||||
t.Fatalf("dynamic secret eligibility issue = %#v", issue)
|
t.Fatalf("dynamic secret eligibility issue = %#v", issue)
|
||||||
}
|
}
|
||||||
|
if len(secretOperation.approvedDynamic) != 0 {
|
||||||
|
t.Fatalf("secret-bearing dynamic descriptor was cached as eligible: %#v", secretOperation.approvedDynamic)
|
||||||
|
}
|
||||||
for _, current := range []reflect.Type{reflect.TypeFor[secretDynamicRoot](), reflect.TypeFor[secretDynamicNested](), reflect.TypeFor[Secret]()} {
|
for _, current := range []reflect.Type{reflect.TypeFor[secretDynamicRoot](), reflect.TypeFor[secretDynamicNested](), reflect.TypeFor[Secret]()} {
|
||||||
if _, exists := secretSchema.types[current]; exists {
|
if _, exists := secretSchema.types[current]; exists {
|
||||||
t.Errorf("secret eligibility failure retained provisional descriptor for %v", current)
|
t.Errorf("secret eligibility failure retained provisional descriptor for %v", current)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type wideDynamicEligibilityValue struct {
|
||||||
|
Alpha string
|
||||||
|
Bravo string
|
||||||
|
Charlie string
|
||||||
|
Delta string
|
||||||
|
Echo string
|
||||||
|
Foxtrot string
|
||||||
|
Golf string
|
||||||
|
Hotel string
|
||||||
|
India string
|
||||||
|
Juliet string
|
||||||
|
Kilo string
|
||||||
|
Lima string
|
||||||
|
Mike string
|
||||||
|
November string
|
||||||
|
Oscar string
|
||||||
|
Papa string
|
||||||
|
}
|
||||||
|
|
||||||
|
type repeatedNilDynamicEligibilityConfig struct {
|
||||||
|
Values []any
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDynamicEligibilityIsReusedWithinProjectionOperation(t *testing.T) {
|
||||||
|
schema, schemaIssue := buildSchema(reflect.TypeFor[repeatedNilDynamicEligibilityConfig](), testSecretBinding())
|
||||||
|
if schemaIssue != nil {
|
||||||
|
t.Fatalf("schema issue = %#v", schemaIssue)
|
||||||
|
}
|
||||||
|
values := make([]any, 32)
|
||||||
|
for index := range values {
|
||||||
|
values[index] = (*wideDynamicEligibilityValue)(nil)
|
||||||
|
}
|
||||||
|
configuration := repeatedNilDynamicEligibilityConfig{Values: values}
|
||||||
|
operation := valueOperation{schema: schema, limits: defaultProjectionLimits(), active: make(map[valueVisit]bool)}
|
||||||
|
node, issue := operation.project(reflect.ValueOf(configuration), schema.root, Path{}, 0)
|
||||||
|
if issue != nil {
|
||||||
|
t.Fatalf("repeated typed-nil projection issue = %#v", issue)
|
||||||
|
}
|
||||||
|
if len(node.Mapping) != 1 || len(node.Mapping[0].Value.Sequence) != len(values) {
|
||||||
|
t.Fatalf("projected repeated values = %#v", node)
|
||||||
|
}
|
||||||
|
for index, child := range node.Mapping[0].Value.Sequence {
|
||||||
|
if child.Kind != document.Null {
|
||||||
|
t.Errorf("projected value %d kind = %v, want null", index, child.Kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current := reflect.TypeFor[*wideDynamicEligibilityValue]()
|
||||||
|
if len(operation.approvedDynamic) != 1 || operation.approvedDynamic[current] == nil ||
|
||||||
|
operation.approvedDynamic[current] != schema.types[current] {
|
||||||
|
t.Fatalf("operation eligibility cache = %#v", operation.approvedDynamic)
|
||||||
|
}
|
||||||
|
otherOperation := valueOperation{schema: schema, limits: defaultProjectionLimits(), active: make(map[valueVisit]bool)}
|
||||||
|
if len(otherOperation.approvedDynamic) != 0 {
|
||||||
|
t.Fatalf("dynamic eligibility leaked into another operation: %#v", otherOperation.approvedDynamic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,13 +24,14 @@ type valueVisit struct {
|
|||||||
length int
|
length int
|
||||||
}
|
}
|
||||||
|
|
||||||
// valueOperation owns one traversal's cycle and budget state and reuses the
|
// valueOperation owns one traversal's cycle and budget state, and caches
|
||||||
// current load's descriptor cache for dynamic values.
|
// dynamic descriptors approved for that traversal.
|
||||||
type valueOperation struct {
|
type valueOperation struct {
|
||||||
cloneMode secretCloneMode
|
cloneMode secretCloneMode
|
||||||
schema *schemaDescriptor
|
schema *schemaDescriptor
|
||||||
limits projectionLimits
|
limits projectionLimits
|
||||||
active map[valueVisit]bool
|
active map[valueVisit]bool
|
||||||
|
approvedDynamic map[reflect.Type]*typeDescriptor
|
||||||
nodes int
|
nodes int
|
||||||
nodeCredits int
|
nodeCredits int
|
||||||
}
|
}
|
||||||
@@ -39,6 +40,9 @@ func (o *valueOperation) dynamicDescriptor(current reflect.Type, path Path) (*ty
|
|||||||
if o.schema.secrets.Type != nil && current == o.schema.secrets.Type {
|
if o.schema.secrets.Type != nil && current == o.schema.secrets.Type {
|
||||||
return nil, &projectionIssue{code: CodeSchemaDynamicType, path: path.clone(), message: "dynamic configuration values cannot establish secret-reference semantics"}
|
return nil, &projectionIssue{code: CodeSchemaDynamicType, path: path.clone(), message: "dynamic configuration values cannot establish secret-reference semantics"}
|
||||||
}
|
}
|
||||||
|
if descriptor, approved := o.approvedDynamic[current]; approved {
|
||||||
|
return descriptor, nil
|
||||||
|
}
|
||||||
builder := schemaBuilder{cache: o.schema.types, building: make(map[reflect.Type]bool), secrets: o.schema.secrets}
|
builder := schemaBuilder{cache: o.schema.types, building: make(map[reflect.Type]bool), secrets: o.schema.secrets}
|
||||||
descriptor, issue := builder.describe(current, path)
|
descriptor, issue := builder.describe(current, path)
|
||||||
if issue != nil {
|
if issue != nil {
|
||||||
@@ -49,6 +53,10 @@ func (o *valueOperation) dynamicDescriptor(current reflect.Type, path Path) (*ty
|
|||||||
builder.rollback()
|
builder.rollback()
|
||||||
return nil, &projectionIssue{code: CodeSchemaDynamicType, path: path.clone(), message: "dynamic configuration values cannot establish secret-reference semantics"}
|
return nil, &projectionIssue{code: CodeSchemaDynamicType, path: path.clone(), message: "dynamic configuration values cannot establish secret-reference semantics"}
|
||||||
}
|
}
|
||||||
|
if o.approvedDynamic == nil {
|
||||||
|
o.approvedDynamic = make(map[reflect.Type]*typeDescriptor)
|
||||||
|
}
|
||||||
|
o.approvedDynamic[current] = descriptor
|
||||||
return descriptor, nil
|
return descriptor, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user