go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/internal/config/compiledcfg/config.go (about) 1 // Copyright 2022 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package compiledcfg contains compiled versions of the LUCI Analysis config. 16 // (E.g. Regular expressions are compiled for efficiency.) 17 package compiledcfg 18 19 import ( 20 "context" 21 "regexp" 22 "time" 23 24 "go.chromium.org/luci/common/data/caching/lru" 25 "go.chromium.org/luci/common/errors" 26 "go.chromium.org/luci/server/caching" 27 28 "go.chromium.org/luci/analysis/internal/clustering/algorithms/testname/rules" 29 "go.chromium.org/luci/analysis/internal/config" 30 configpb "go.chromium.org/luci/analysis/proto/config" 31 ) 32 33 // TODO(crbug.com/1243174). Instrument the size of this cache so that we 34 // can monitor it. 35 var configCache = caching.RegisterLRUCache[string, *ProjectConfig](0) 36 37 // ProjectConfig is a compiled version of LUCI Analysis project configuration. 38 type ProjectConfig struct { 39 // Config is the raw, uncompiled, configuration. 40 Config *configpb.ProjectConfig 41 42 // TestNameRules are the set of rules to use to cluster test results 43 // by test name. 44 TestNameRules []rules.Evaluator 45 46 // ReasonMaskPatterns is the set of patterns to use to mask out parts 47 // of the failure reason before clustering. 48 ReasonMaskPatterns []*regexp.Regexp 49 50 // LastUpdated is the time the configuration was last updated. 51 LastUpdated time.Time 52 } 53 54 // NewConfig compiles the given clustering configuration into a Config 55 // object. 56 func NewConfig(config *configpb.ProjectConfig) (*ProjectConfig, error) { 57 rs := config.Clustering.GetTestNameRules() 58 compiledRules := make([]rules.Evaluator, len(rs)) 59 for i, rule := range rs { 60 eval, err := rules.Compile(rule) 61 if err != nil { 62 return nil, errors.Annotate(err, "compiling test name clustering rule").Err() 63 } 64 compiledRules[i] = eval 65 } 66 rmps := config.Clustering.GetReasonMaskPatterns() 67 compiledReasonMaskPatterns := make([]*regexp.Regexp, len(rmps)) 68 for i, p := range rmps { 69 re, err := regexp.Compile(p) 70 if err != nil { 71 return nil, errors.Annotate(err, "compiling reason mask pattern").Err() 72 } 73 compiledReasonMaskPatterns[i] = re 74 } 75 76 return &ProjectConfig{ 77 Config: config, 78 TestNameRules: compiledRules, 79 ReasonMaskPatterns: compiledReasonMaskPatterns, 80 LastUpdated: config.LastUpdated.AsTime(), 81 }, nil 82 } 83 84 // Project returns the clustering configuration for the given project, 85 // with a LastUpdated time of at least minimumVersion. If no particular 86 // minimum version is desired, pass time.Time{} to minimumVersion. 87 func Project(ctx context.Context, project string, minimumVersion time.Time) (*ProjectConfig, error) { 88 cache := configCache.LRU(ctx) 89 if cache == nil { 90 // A fallback useful in unit tests that may not have the process cache 91 // available. Production environments usually have the cache installed 92 // by the framework code that initializes the root context. 93 projectCfg, err := config.ProjectWithMinimumVersion(ctx, project, minimumVersion) 94 if err != nil { 95 return nil, err 96 } 97 config, err := NewConfig(projectCfg) 98 if err != nil { 99 return nil, err 100 } 101 return config, nil 102 } else { 103 var err error 104 val, _ := cache.Mutate(ctx, project, func(it *lru.Item[*ProjectConfig]) *lru.Item[*ProjectConfig] { 105 var projectCfg *configpb.ProjectConfig 106 // Fetch the latest configuration for the given project, with 107 // the specified minimum version. 108 projectCfg, err = config.ProjectWithMinimumVersion(ctx, project, minimumVersion) 109 if err != nil { 110 // Delete cached value. 111 return nil 112 } 113 114 if it != nil { 115 cfg := it.Value 116 if cfg.LastUpdated.Equal(projectCfg.LastUpdated.AsTime()) { 117 // Cached value is already up to date. 118 return it 119 } 120 } 121 var config *ProjectConfig 122 config, err = NewConfig(projectCfg) 123 if err != nil { 124 // Delete cached value. 125 return nil 126 } 127 return &lru.Item[*ProjectConfig]{ 128 Value: config, 129 Exp: 0, // No expiry. 130 } 131 }) 132 if err != nil { 133 return nil, errors.Annotate(err, "obtain compiled configuration").Err() 134 } 135 return val, nil 136 } 137 }