Skip to content

Commit 69f070e

Browse files
committed
fix: fixed memory pools & race issues
* fixes * a memory leak with recycled *Result in schema_props.go was causing some invalid results when GC is under pressure (seen on go-swagger test) * refactored schema props to isolate anyOf, oneOf, allOf, etc. * made the validation logic a little clearer to follow * fixed leaks with unreleased allocated Results * ensured that child validators for schemaPropsValidator cannot be reused after they are exhausted * ensured that recyclable results are redeemed in all cases in default and example validators * with go1.22, the race detector figured a race condition when expanding the swagger schema for parameters (spec.go) * before iterating over parameters to validate the swagger schema for a parameter, deep clone the schema from the root spec. This is performed once for a spec, so the perf impact should be negligible * tests * added test for go-swagger fixture #2866, which exhibits the failure with the highest probability * added a debug version of the pools (build with the "validatedebug" build tag) to assert more thorough checks of the correctness of the pools usage Signed-off-by: Frederic BIDON <[email protected]>
1 parent f563d1c commit 69f070e

20 files changed

+11116
-338
lines changed

default_validator.go

+16-4
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func (d *defaultValidator) isVisited(path string) bool {
8383

8484
// Validate validates the default values declared in the swagger spec
8585
func (d *defaultValidator) Validate() *Result {
86-
errs := poolOfResults.BorrowResult() // will redeem when merged
86+
errs := pools.poolOfResults.BorrowResult() // will redeem when merged
8787

8888
if d == nil || d.SpecValidator == nil {
8989
return errs
@@ -97,7 +97,7 @@ func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
9797
// every default value that is specified must validate against the schema for that property
9898
// headers, items, parameters, schema
9999

100-
res := poolOfResults.BorrowResult() // will redeem when merged
100+
res := pools.poolOfResults.BorrowResult() // will redeem when merged
101101
s := d.SpecValidator
102102

103103
for method, pathItem := range s.expandedAnalyzer().Operations() {
@@ -119,6 +119,8 @@ func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
119119
if red.HasErrorsOrWarnings() {
120120
res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In))
121121
res.Merge(red)
122+
} else if red.wantsRedeemOnMerge {
123+
pools.poolOfResults.RedeemResult(red)
122124
}
123125
}
124126

@@ -128,6 +130,8 @@ func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
128130
if red.HasErrorsOrWarnings() {
129131
res.AddErrors(defaultValueItemsDoesNotValidateMsg(param.Name, param.In))
130132
res.Merge(red)
133+
} else if red.wantsRedeemOnMerge {
134+
pools.poolOfResults.RedeemResult(red)
131135
}
132136
}
133137

@@ -137,6 +141,8 @@ func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
137141
if red.HasErrorsOrWarnings() {
138142
res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In))
139143
res.Merge(red)
144+
} else if red.wantsRedeemOnMerge {
145+
pools.poolOfResults.RedeemResult(red)
140146
}
141147
}
142148
}
@@ -188,6 +194,8 @@ func (d *defaultValidator) validateDefaultInResponse(resp *spec.Response, respon
188194
if red.HasErrorsOrWarnings() {
189195
res.AddErrors(defaultValueHeaderDoesNotValidateMsg(operationID, nm, responseName))
190196
res.Merge(red)
197+
} else if red.wantsRedeemOnMerge {
198+
pools.poolOfResults.RedeemResult(red)
191199
}
192200
}
193201

@@ -197,6 +205,8 @@ func (d *defaultValidator) validateDefaultInResponse(resp *spec.Response, respon
197205
if red.HasErrorsOrWarnings() {
198206
res.AddErrors(defaultValueHeaderItemsDoesNotValidateMsg(operationID, nm, responseName))
199207
res.Merge(red)
208+
} else if red.wantsRedeemOnMerge {
209+
pools.poolOfResults.RedeemResult(red)
200210
}
201211
}
202212

@@ -216,6 +226,8 @@ func (d *defaultValidator) validateDefaultInResponse(resp *spec.Response, respon
216226
// Additional message to make sure the context of the error is not lost
217227
res.AddErrors(defaultValueInDoesNotValidateMsg(operationID, responseName))
218228
res.Merge(red)
229+
} else if red.wantsRedeemOnMerge {
230+
pools.poolOfResults.RedeemResult(red)
219231
}
220232
}
221233
return res
@@ -227,7 +239,7 @@ func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in stri
227239
return nil
228240
}
229241
d.beingVisited(path)
230-
res := poolOfResults.BorrowResult()
242+
res := pools.poolOfResults.BorrowResult()
231243
s := d.SpecValidator
232244

233245
if schema.Default != nil {
@@ -273,7 +285,7 @@ func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in stri
273285
// TODO: Temporary duplicated code. Need to refactor with examples
274286

275287
func (d *defaultValidator) validateDefaultValueItemsAgainstSchema(path, in string, root interface{}, items *spec.Items) *Result {
276-
res := poolOfResults.BorrowResult()
288+
res := pools.poolOfResults.BorrowResult()
277289
s := d.SpecValidator
278290
if items != nil {
279291
if items.Default != nil {

example_validator.go

+16-8
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func (ex *exampleValidator) isVisited(path string) bool {
5959
// - individual property
6060
// - responses
6161
func (ex *exampleValidator) Validate() *Result {
62-
errs := poolOfResults.BorrowResult()
62+
errs := pools.poolOfResults.BorrowResult()
6363

6464
if ex == nil || ex.SpecValidator == nil {
6565
return errs
@@ -75,7 +75,7 @@ func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result {
7575
// in: schemas, properties, object, items
7676
// not in: headers, parameters without schema
7777

78-
res := poolOfResults.BorrowResult()
78+
res := pools.poolOfResults.BorrowResult()
7979
s := ex.SpecValidator
8080

8181
for method, pathItem := range s.expandedAnalyzer().Operations() {
@@ -97,6 +97,8 @@ func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result {
9797
if red.HasErrorsOrWarnings() {
9898
res.AddWarnings(exampleValueDoesNotValidateMsg(param.Name, param.In))
9999
res.MergeAsWarnings(red)
100+
} else if red.wantsRedeemOnMerge {
101+
pools.poolOfResults.RedeemResult(red)
100102
}
101103
}
102104

@@ -106,8 +108,8 @@ func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result {
106108
if red.HasErrorsOrWarnings() {
107109
res.AddWarnings(exampleValueItemsDoesNotValidateMsg(param.Name, param.In))
108110
res.Merge(red)
109-
} else {
110-
poolOfResults.RedeemResult(red)
111+
} else if red.wantsRedeemOnMerge {
112+
pools.poolOfResults.RedeemResult(red)
111113
}
112114
}
113115

@@ -117,8 +119,8 @@ func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result {
117119
if red.HasErrorsOrWarnings() {
118120
res.AddWarnings(exampleValueDoesNotValidateMsg(param.Name, param.In))
119121
res.Merge(red)
120-
} else {
121-
poolOfResults.RedeemResult(red)
122+
} else if red.wantsRedeemOnMerge {
123+
pools.poolOfResults.RedeemResult(red)
122124
}
123125
}
124126
}
@@ -170,6 +172,8 @@ func (ex *exampleValidator) validateExampleInResponse(resp *spec.Response, respo
170172
if red.HasErrorsOrWarnings() {
171173
res.AddWarnings(exampleValueHeaderDoesNotValidateMsg(operationID, nm, responseName))
172174
res.MergeAsWarnings(red)
175+
} else if red.wantsRedeemOnMerge {
176+
pools.poolOfResults.RedeemResult(red)
173177
}
174178
}
175179

@@ -179,6 +183,8 @@ func (ex *exampleValidator) validateExampleInResponse(resp *spec.Response, respo
179183
if red.HasErrorsOrWarnings() {
180184
res.AddWarnings(exampleValueHeaderItemsDoesNotValidateMsg(operationID, nm, responseName))
181185
res.MergeAsWarnings(red)
186+
} else if red.wantsRedeemOnMerge {
187+
pools.poolOfResults.RedeemResult(red)
182188
}
183189
}
184190

@@ -198,6 +204,8 @@ func (ex *exampleValidator) validateExampleInResponse(resp *spec.Response, respo
198204
// Additional message to make sure the context of the error is not lost
199205
res.AddWarnings(exampleValueInDoesNotValidateMsg(operationID, responseName))
200206
res.Merge(red)
207+
} else if red.wantsRedeemOnMerge {
208+
pools.poolOfResults.RedeemResult(red)
201209
}
202210
}
203211

@@ -225,7 +233,7 @@ func (ex *exampleValidator) validateExampleValueSchemaAgainstSchema(path, in str
225233
}
226234
ex.beingVisited(path)
227235
s := ex.SpecValidator
228-
res := poolOfResults.BorrowResult()
236+
res := pools.poolOfResults.BorrowResult()
229237

230238
if schema.Example != nil {
231239
res.MergeAsWarnings(
@@ -271,7 +279,7 @@ func (ex *exampleValidator) validateExampleValueSchemaAgainstSchema(path, in str
271279
//
272280

273281
func (ex *exampleValidator) validateExampleValueItemsAgainstSchema(path, in string, root interface{}, items *spec.Items) *Result {
274-
res := poolOfResults.BorrowResult()
282+
res := pools.poolOfResults.BorrowResult()
275283
s := ex.SpecValidator
276284
if items != nil {
277285
if items.Example != nil {

0 commit comments

Comments
 (0)