Skip to content

Commit 395536c

Browse files
authored
Add parallel validation execution (#130)
* fix: make string repr of expressionUsesOnlyAllowedLabelsForMetricRegexp stable Signed-off-by: Martin Chodur <m.chodur@seznam.cz> * feat: make validation parallel Signed-off-by: Martin Chodur <m.chodur@seznam.cz> * fix: make the reports predictable and sorted Signed-off-by: Martin Chodur <m.chodur@seznam.cz> * fix: changelog Signed-off-by: Martin Chodur <m.chodur@seznam.cz> * Apply suggestion from @FUSAKLA --------- Signed-off-by: Martin Chodur <m.chodur@seznam.cz>
1 parent 106047c commit 395536c

File tree

4 files changed

+222
-128
lines changed

4 files changed

+222
-128
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
- Changed: Validations are now executed in parallel to speed up the validation process.
11+
- Changed: Sort the reports fiels, groups, rules by names for a predictable output.
12+
1013
## [3.10.0] - 2025-11-11
1114
- :warning: Changed: `params` field of all validators from now on **does not allow unknown fields**, to avoid typos and mistakes.
1215
- Added: New param `negative` to all validators that does regexp matching named `*MatchesRegexp` to invert the matching logic.

pkg/report/report.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import (
55
"encoding/json"
66
"errors"
77
"fmt"
8+
"slices"
9+
"strings"
10+
"sync"
811
"time"
912

1013
"github.com/fusakla/promruval/v3/pkg/config"
@@ -76,6 +79,8 @@ type ValidationReport struct {
7679
ValidationRules []ValidationRule `json:"validation_rules" yaml:"validation_rules"`
7780

7881
FilesReports []*FileReport `json:"files_reports" yaml:"files_reports"`
82+
83+
mu sync.Mutex `json:"-" yaml:"-"`
7984
}
8085

8186
func (r *ValidationReport) NewFileReport(name string) *FileReport {
@@ -85,17 +90,38 @@ func (r *ValidationReport) NewFileReport(name string) *FileReport {
8590
Errors: []*Error{},
8691
GroupReports: []*GroupReport{},
8792
}
93+
r.mu.Lock()
8894
r.FilesReports = append(r.FilesReports, &newReport)
95+
r.mu.Unlock()
8996
return &newReport
9097
}
9198

99+
// Sort sorts all reports (files, groups, rules) for predictable output.
100+
func (r *ValidationReport) Sort() {
101+
slices.SortFunc(r.FilesReports, func(a, b *FileReport) int {
102+
return strings.Compare(a.Name, b.Name)
103+
})
104+
for _, fileReport := range r.FilesReports {
105+
slices.SortFunc(fileReport.GroupReports, func(a, b *GroupReport) int {
106+
return strings.Compare(a.Name, b.Name)
107+
})
108+
for _, groupReport := range fileReport.GroupReports {
109+
slices.SortFunc(groupReport.RuleReports, func(a, b *RuleReport) int {
110+
return strings.Compare(a.Name, b.Name)
111+
})
112+
}
113+
}
114+
}
115+
92116
type FileReport struct {
93117
Name string `json:"file_name" yaml:"file_name"`
94118
Valid bool `json:"valid" yaml:"valid"`
95119
Excluded bool `json:"excluded" yaml:"excluded"`
96120
Errors []*Error `json:"errors" yaml:"errors"`
97121
HasRuleValidationErrors bool `json:"has_rule_validation_errors" yaml:"has_rule_validation_errors"`
98122
GroupReports []*GroupReport `json:"group_reports" yaml:"group_reports"`
123+
124+
mu sync.Mutex `json:"-" yaml:"-"`
99125
}
100126

101127
func (r *FileReport) NewGroupReport(name string) *GroupReport {
@@ -105,7 +131,9 @@ func (r *FileReport) NewGroupReport(name string) *GroupReport {
105131
RuleReports: []*RuleReport{},
106132
Errors: []*Error{},
107133
}
134+
r.mu.Lock()
108135
r.GroupReports = append(r.GroupReports, &newReport)
136+
r.mu.Unlock()
109137
return &newReport
110138
}
111139

@@ -129,6 +157,8 @@ type GroupReport struct {
129157
Excluded bool `json:"excluded" yaml:"excluded"`
130158
RuleReports []*RuleReport `json:"rule_reports" yaml:"rule_reports"`
131159
Errors []*Error `json:"errors" yaml:"errors"`
160+
161+
mu sync.Mutex `json:"-" yaml:"-"`
132162
}
133163

134164
func (r *GroupReport) NewRuleReport(name string, ruleType config.ValidationScope) *RuleReport {
@@ -138,7 +168,9 @@ func (r *GroupReport) NewRuleReport(name string, ruleType config.ValidationScope
138168
RuleType: ruleType,
139169
Errors: []*Error{},
140170
}
171+
r.mu.Lock()
141172
r.RuleReports = append(r.RuleReports, &newReport)
173+
r.mu.Unlock()
142174
return &newReport
143175
}
144176

@@ -191,6 +223,8 @@ func (r *RuleReport) AsText(output *IndentedOutput) {
191223
}
192224

193225
func (r *ValidationReport) AsText(indentationStep int, color bool) (string, error) {
226+
r.Sort()
227+
194228
output := NewIndentedOutput(indentationStep, color)
195229
validationText, err := ValidationDocs(r.ValidationRules, "text")
196230
if err != nil {
@@ -227,6 +261,8 @@ func renderStatistic(objectType string, total, excluded int) string {
227261
}
228262

229263
func (r *ValidationReport) AsJSON() (string, error) {
264+
r.Sort()
265+
230266
b, err := json.MarshalIndent(r, "", " ")
231267
if err != nil {
232268
return "", err
@@ -236,6 +272,8 @@ func (r *ValidationReport) AsJSON() (string, error) {
236272
}
237273

238274
func (r *ValidationReport) AsYaml() (string, error) {
275+
r.Sort()
276+
239277
b, err := yaml.Marshal(r)
240278
if err != nil {
241279
return "", err

0 commit comments

Comments
 (0)