-
Notifications
You must be signed in to change notification settings - Fork 53
🐛 normalize paths in cache #864
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
d5279c7
7893237
07a2e1c
299ecf0
6b28be5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -33,13 +33,6 @@ var ( | |||||||||||||||||||||||||||||||||||||
| tracer = otel.Tracer(name) | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| type cacheValue struct { | ||||||||||||||||||||||||||||||||||||||
| incident konveyor.Incident | ||||||||||||||||||||||||||||||||||||||
| ViolationName string | ||||||||||||||||||||||||||||||||||||||
| violation konveyor.Violation | ||||||||||||||||||||||||||||||||||||||
| ruleset konveyor.RuleSet | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| type Analyzer interface { | ||||||||||||||||||||||||||||||||||||||
| Analyze(client *rpc.Client, args Args, response *Response) error | ||||||||||||||||||||||||||||||||||||||
| NotifyFileChanges(client *rpc.Client, changes NotifyFileChangesArgs, response *NotifyFileChangesResponse) error | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -67,8 +60,7 @@ type analyzer struct { | |||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| discoveryCacheMutex sync.Mutex | ||||||||||||||||||||||||||||||||||||||
| discoveryCache []konveyor.RuleSet | ||||||||||||||||||||||||||||||||||||||
| cache map[string][]cacheValue | ||||||||||||||||||||||||||||||||||||||
| cacheMutex sync.RWMutex | ||||||||||||||||||||||||||||||||||||||
| cache IncidentsCache | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| contextLines int | ||||||||||||||||||||||||||||||||||||||
| location string | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -182,8 +174,7 @@ func NewAnalyzer(limitIncidents, limitCodeSnips, contextLines int, location, inc | |||||||||||||||||||||||||||||||||||||
| violationRulesets: violationRulesets, | ||||||||||||||||||||||||||||||||||||||
| discoveryCache: []konveyor.RuleSet{}, | ||||||||||||||||||||||||||||||||||||||
| discoveryCacheMutex: sync.Mutex{}, | ||||||||||||||||||||||||||||||||||||||
| cache: map[string][]cacheValue{}, | ||||||||||||||||||||||||||||||||||||||
| cacheMutex: sync.RWMutex{}, | ||||||||||||||||||||||||||||||||||||||
| cache: NewIncidentsCache(log), | ||||||||||||||||||||||||||||||||||||||
| }, nil | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -270,7 +261,7 @@ func (a *analyzer) Analyze(client *rpc.Client, args Args, response *Response) er | |||||||||||||||||||||||||||||||||||||
| // Then we should return early, with results from the cache | ||||||||||||||||||||||||||||||||||||||
| if len(scopes) == 0 && !args.ResetCache { | ||||||||||||||||||||||||||||||||||||||
| a.Logger.Info("no scopes and not resetting cache, return early with results from cache") | ||||||||||||||||||||||||||||||||||||||
| a.Logger.Info("Current cache len", len(a.cache)) | ||||||||||||||||||||||||||||||||||||||
| a.Logger.Info("Current cache len", a.cache.Len()) | ||||||||||||||||||||||||||||||||||||||
| response.Rulesets = a.createRulesetsFromCache() | ||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -305,6 +296,8 @@ func (a *analyzer) Analyze(client *rpc.Client, args Args, response *Response) er | |||||||||||||||||||||||||||||||||||||
| return rulesets[i].Name < rulesets[j].Name | ||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| a.Logger.Info("[pg] rulesets", "rulesets", rulesets) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // This is a full run, set the complete new results | ||||||||||||||||||||||||||||||||||||||
| if len(args.IncludedPaths) == 0 { | ||||||||||||||||||||||||||||||||||||||
| a.Logger.V(5).Info("setting cache for full run") | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -333,66 +326,39 @@ func (a *analyzer) NotifyFileChanges(client *rpc.Client, args NotifyFileChangesA | |||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| func (a *analyzer) setCache(rulesets []konveyor.RuleSet) { | ||||||||||||||||||||||||||||||||||||||
| a.cacheMutex.Lock() | ||||||||||||||||||||||||||||||||||||||
| defer a.cacheMutex.Unlock() | ||||||||||||||||||||||||||||||||||||||
| a.cache = map[string][]cacheValue{} | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| a.cache = NewIncidentsCache(a.Logger) | ||||||||||||||||||||||||||||||||||||||
| a.addRulesetsToCache(rulesets) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| func (a *analyzer) updateCache(rulesets []konveyor.RuleSet, includedPaths []string) { | ||||||||||||||||||||||||||||||||||||||
| a.cacheMutex.Lock() | ||||||||||||||||||||||||||||||||||||||
| defer a.cacheMutex.Unlock() | ||||||||||||||||||||||||||||||||||||||
| if includedPaths != nil { | ||||||||||||||||||||||||||||||||||||||
| a.invalidateCachePerFile(includedPaths) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| a.addRulesetsToCache(rulesets) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| func (a *analyzer) addRulesetsToCache(rulesets []konveyor.RuleSet) { | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| for _, r := range rulesets { | ||||||||||||||||||||||||||||||||||||||
| for violationName, v := range r.Violations { | ||||||||||||||||||||||||||||||||||||||
| for _, i := range v.Incidents { | ||||||||||||||||||||||||||||||||||||||
| a.Logger.V(8).Info("here update cache incident", "incident", i) | ||||||||||||||||||||||||||||||||||||||
| if l, ok := a.cache[i.URI.Filename()]; ok { | ||||||||||||||||||||||||||||||||||||||
| l = append(l, cacheValue{ | ||||||||||||||||||||||||||||||||||||||
| incident: i, | ||||||||||||||||||||||||||||||||||||||
| violation: konveyor.Violation{ | ||||||||||||||||||||||||||||||||||||||
| Description: v.Description, | ||||||||||||||||||||||||||||||||||||||
| Category: v.Category, | ||||||||||||||||||||||||||||||||||||||
| Labels: v.Labels, | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| ViolationName: violationName, | ||||||||||||||||||||||||||||||||||||||
| ruleset: konveyor.RuleSet{ | ||||||||||||||||||||||||||||||||||||||
| Name: r.Name, | ||||||||||||||||||||||||||||||||||||||
| Description: r.Description, | ||||||||||||||||||||||||||||||||||||||
| Tags: r.Tags, | ||||||||||||||||||||||||||||||||||||||
| Unmatched: r.Unmatched, | ||||||||||||||||||||||||||||||||||||||
| Skipped: r.Skipped, | ||||||||||||||||||||||||||||||||||||||
| Errors: r.Errors, | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||
| a.cache[i.URI.Filename()] = l | ||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||
| a.cache[i.URI.Filename()] = []cacheValue{{ | ||||||||||||||||||||||||||||||||||||||
| incident: i, | ||||||||||||||||||||||||||||||||||||||
| violation: konveyor.Violation{ | ||||||||||||||||||||||||||||||||||||||
| Description: v.Description, | ||||||||||||||||||||||||||||||||||||||
| Category: v.Category, | ||||||||||||||||||||||||||||||||||||||
| Labels: v.Labels, | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| ViolationName: violationName, | ||||||||||||||||||||||||||||||||||||||
| ruleset: konveyor.RuleSet{ | ||||||||||||||||||||||||||||||||||||||
| Name: r.Name, | ||||||||||||||||||||||||||||||||||||||
| Description: r.Description, | ||||||||||||||||||||||||||||||||||||||
| Tags: r.Tags, | ||||||||||||||||||||||||||||||||||||||
| Unmatched: r.Unmatched, | ||||||||||||||||||||||||||||||||||||||
| Skipped: r.Skipped, | ||||||||||||||||||||||||||||||||||||||
| Errors: r.Errors, | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| a.Logger.Info("here update cache incident", "incident", i) | ||||||||||||||||||||||||||||||||||||||
| a.cache.Add(i.URI.Filename(), CacheValue{ | ||||||||||||||||||||||||||||||||||||||
| Incident: i, | ||||||||||||||||||||||||||||||||||||||
| Violation: konveyor.Violation{ | ||||||||||||||||||||||||||||||||||||||
| Description: v.Description, | ||||||||||||||||||||||||||||||||||||||
| Category: v.Category, | ||||||||||||||||||||||||||||||||||||||
| Labels: v.Labels, | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| ViolationName: violationName, | ||||||||||||||||||||||||||||||||||||||
| Ruleset: konveyor.RuleSet{ | ||||||||||||||||||||||||||||||||||||||
| Name: r.Name, | ||||||||||||||||||||||||||||||||||||||
| Description: r.Description, | ||||||||||||||||||||||||||||||||||||||
| Tags: r.Tags, | ||||||||||||||||||||||||||||||||||||||
| Unmatched: r.Unmatched, | ||||||||||||||||||||||||||||||||||||||
| Skipped: r.Skipped, | ||||||||||||||||||||||||||||||||||||||
| Errors: r.Errors, | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -401,34 +367,30 @@ func (a *analyzer) addRulesetsToCache(rulesets []konveyor.RuleSet) { | |||||||||||||||||||||||||||||||||||||
| func (a *analyzer) invalidateCachePerFile(paths []string) { | ||||||||||||||||||||||||||||||||||||||
| for _, p := range paths { | ||||||||||||||||||||||||||||||||||||||
| a.Logger.Info("deleting cache entry for path", "path", p) | ||||||||||||||||||||||||||||||||||||||
| delete(a.cache, p) | ||||||||||||||||||||||||||||||||||||||
| a.cache.Delete(p) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
367
to
372
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Normalize invalidate paths to analyzer root before deletion. If clients send relative paths, deletion may miss absolute keys. Normalize to absolute (relative to func (a *analyzer) invalidateCachePerFile(paths []string) {
for _, p := range paths {
- a.Logger.Info("deleting cache entry for path", "path", p)
- a.cache.Delete(p)
+ norm := p
+ if !filepath.IsAbs(norm) {
+ norm = filepath.Join(a.location, norm)
+ }
+ norm = filepath.Clean(norm)
+ a.Logger.Info("deleting cache entry for path", "path", p, "normalized", norm)
+ a.cache.Delete(norm)
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @coderabbitai this is not needed as we never get filepaths outside workspace from the analyzer
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @pranavgaikwad, thank you for the clarification! I understand that the analyzer ensures all file paths are consistently within the workspace, making the normalization step unnecessary. 🐰 ✏️ Learnings added
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| func (a *analyzer) createRulesetsFromCache() []konveyor.RuleSet { | ||||||||||||||||||||||||||||||||||||||
| a.cacheMutex.RLock() | ||||||||||||||||||||||||||||||||||||||
| defer a.cacheMutex.RUnlock() | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ruleSetMap := map[string]konveyor.RuleSet{} | ||||||||||||||||||||||||||||||||||||||
| a.Logger.V(8).Info("cache", "cacheVal", a.cache) | ||||||||||||||||||||||||||||||||||||||
| for filePath, cacheValue := range a.cache { | ||||||||||||||||||||||||||||||||||||||
| for filePath, cacheValue := range a.cache.Entries() { | ||||||||||||||||||||||||||||||||||||||
| for _, v := range cacheValue { | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if ruleset, ok := ruleSetMap[v.ruleset.Name]; ok { | ||||||||||||||||||||||||||||||||||||||
| if ruleset, ok := ruleSetMap[v.Ruleset.Name]; ok { | ||||||||||||||||||||||||||||||||||||||
| if vio, ok := ruleset.Violations[v.ViolationName]; ok { | ||||||||||||||||||||||||||||||||||||||
| vio.Incidents = append(vio.Incidents, v.incident) | ||||||||||||||||||||||||||||||||||||||
| vio.Incidents = append(vio.Incidents, v.Incident) | ||||||||||||||||||||||||||||||||||||||
| ruleset.Violations[v.ViolationName] = vio | ||||||||||||||||||||||||||||||||||||||
| ruleSetMap[ruleset.Name] = ruleset | ||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||
| violation := v.violation | ||||||||||||||||||||||||||||||||||||||
| violation.Incidents = []konveyor.Incident{v.incident} | ||||||||||||||||||||||||||||||||||||||
| violation := v.Violation | ||||||||||||||||||||||||||||||||||||||
| violation.Incidents = []konveyor.Incident{v.Incident} | ||||||||||||||||||||||||||||||||||||||
| ruleset.Violations[v.ViolationName] = violation | ||||||||||||||||||||||||||||||||||||||
| ruleSetMap[ruleset.Name] = ruleset | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||
| violation := v.violation | ||||||||||||||||||||||||||||||||||||||
| violation.Incidents = []konveyor.Incident{v.incident} | ||||||||||||||||||||||||||||||||||||||
| ruleset := v.ruleset | ||||||||||||||||||||||||||||||||||||||
| violation := v.Violation | ||||||||||||||||||||||||||||||||||||||
| violation.Incidents = []konveyor.Incident{v.Incident} | ||||||||||||||||||||||||||||||||||||||
| ruleset := v.Ruleset | ||||||||||||||||||||||||||||||||||||||
| ruleset.Violations = map[string]konveyor.Violation{ | ||||||||||||||||||||||||||||||||||||||
| v.ViolationName: violation, | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,104 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| package service | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||
| "path/filepath" | ||||||||||||||||||||||||||||||||||||||||||||
| "strings" | ||||||||||||||||||||||||||||||||||||||||||||
| "sync" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| "github.com/go-logr/logr" | ||||||||||||||||||||||||||||||||||||||||||||
| "github.com/konveyor/analyzer-lsp/output/v1/konveyor" | ||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| type IncidentsCache interface { | ||||||||||||||||||||||||||||||||||||||||||||
| Get(path string) ([]CacheValue, bool) | ||||||||||||||||||||||||||||||||||||||||||||
| Add(path string, value CacheValue) | ||||||||||||||||||||||||||||||||||||||||||||
| Delete(path string) | ||||||||||||||||||||||||||||||||||||||||||||
| Len() int | ||||||||||||||||||||||||||||||||||||||||||||
| Entries() map[string][]CacheValue | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| type CacheValue struct { | ||||||||||||||||||||||||||||||||||||||||||||
| Incident konveyor.Incident | ||||||||||||||||||||||||||||||||||||||||||||
| ViolationName string | ||||||||||||||||||||||||||||||||||||||||||||
| Violation konveyor.Violation | ||||||||||||||||||||||||||||||||||||||||||||
| Ruleset konveyor.RuleSet | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| func NewIncidentsCache(logger logr.Logger) IncidentsCache { | ||||||||||||||||||||||||||||||||||||||||||||
| return &incidentsCache{ | ||||||||||||||||||||||||||||||||||||||||||||
shawn-hurley marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||
| cache: map[string][]CacheValue{}, | ||||||||||||||||||||||||||||||||||||||||||||
| logger: logger, | ||||||||||||||||||||||||||||||||||||||||||||
| mutex: sync.RWMutex{}, | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| type incidentsCache struct { | ||||||||||||||||||||||||||||||||||||||||||||
| cache map[string][]CacheValue | ||||||||||||||||||||||||||||||||||||||||||||
| logger logr.Logger | ||||||||||||||||||||||||||||||||||||||||||||
| mutex sync.RWMutex | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| func (i *incidentsCache) Len() int { | ||||||||||||||||||||||||||||||||||||||||||||
| return len(i.cache) | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| func (i *incidentsCache) Get(path string) ([]CacheValue, bool) { | ||||||||||||||||||||||||||||||||||||||||||||
| i.mutex.RLock() | ||||||||||||||||||||||||||||||||||||||||||||
| defer i.mutex.RUnlock() | ||||||||||||||||||||||||||||||||||||||||||||
| normalizedPath := normalizePath(path) | ||||||||||||||||||||||||||||||||||||||||||||
| i.logger.V(8).Info("getting cache entry for path", "path", path, "normalizedPath", normalizedPath) | ||||||||||||||||||||||||||||||||||||||||||||
| val, ok := i.cache[normalizedPath] | ||||||||||||||||||||||||||||||||||||||||||||
shawn-hurley marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||
| return val, ok | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Return a cloned slice from Get() to avoid external mutation/aliasing. Exposing the internal slice risks accidental mutation and races. Clone like Entries() does. func (i *incidentsCache) Get(path string) ([]CacheValue, bool) {
i.mutex.RLock()
defer i.mutex.RUnlock()
normalizedPath := normalizePath(path)
i.logger.V(8).Info("getting cache entry for path", "path", path, "normalizedPath", normalizedPath)
- val, ok := i.cache[normalizedPath]
- return val, ok
+ val, ok := i.cache[normalizedPath]
+ if !ok {
+ return nil, false
+ }
+ out := make([]CacheValue, len(val))
+ copy(out, val)
+ return out, true
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that the computer might be correct here, the list will be a pointer as well
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @shawn-hurley You're absolutely right! The slice in Go is indeed a reference type, so returning it directly from the cache exposes the internal data structure to potential mutation by callers. The CodeRabbit suggestion to clone the slice before returning is the correct approach here - it prevents external code from accidentally (or intentionally) modifying the cached data, which could lead to data races and cache integrity issues. The suggested implementation follows the same defensive copying pattern already used in the This is a good catch for maintaining thread safety and data integrity in the cache implementation. 🧠 Learnings used |
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| func (i *incidentsCache) Add(path string, value CacheValue) { | ||||||||||||||||||||||||||||||||||||||||||||
| i.mutex.Lock() | ||||||||||||||||||||||||||||||||||||||||||||
| defer i.mutex.Unlock() | ||||||||||||||||||||||||||||||||||||||||||||
| normalizedPath := normalizePath(path) | ||||||||||||||||||||||||||||||||||||||||||||
| i.logger.V(8).Info("adding cache entry for path", "path", path, "normalizedPath", normalizedPath) | ||||||||||||||||||||||||||||||||||||||||||||
| if _, ok := i.cache[normalizedPath]; !ok { | ||||||||||||||||||||||||||||||||||||||||||||
| i.cache[normalizedPath] = []CacheValue{} | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| i.cache[normalizedPath] = append(i.cache[normalizedPath], value) | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| func (i *incidentsCache) Delete(path string) { | ||||||||||||||||||||||||||||||||||||||||||||
| i.mutex.Lock() | ||||||||||||||||||||||||||||||||||||||||||||
| defer i.mutex.Unlock() | ||||||||||||||||||||||||||||||||||||||||||||
| normalizedPath := normalizePath(path) | ||||||||||||||||||||||||||||||||||||||||||||
| i.logger.V(8).Info("deleting cache entry for path", "path", path, "normalizedPath", normalizedPath) | ||||||||||||||||||||||||||||||||||||||||||||
| delete(i.cache, normalizedPath) | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| func (i *incidentsCache) Keys() []string { | ||||||||||||||||||||||||||||||||||||||||||||
| i.mutex.RLock() | ||||||||||||||||||||||||||||||||||||||||||||
| defer i.mutex.RUnlock() | ||||||||||||||||||||||||||||||||||||||||||||
| keys := make([]string, 0, len(i.cache)) | ||||||||||||||||||||||||||||||||||||||||||||
| for k := range i.cache { | ||||||||||||||||||||||||||||||||||||||||||||
| keys = append(keys, k) | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| return keys | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| func (i *incidentsCache) Entries() map[string][]CacheValue { | ||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we do end up adding a sync, then we probably want to do something here
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. now maknig sure that a clone is returned and not the original map |
||||||||||||||||||||||||||||||||||||||||||||
| i.mutex.RLock() | ||||||||||||||||||||||||||||||||||||||||||||
| defer i.mutex.RUnlock() | ||||||||||||||||||||||||||||||||||||||||||||
| // make sure we never return a reference to original map or any of its slices | ||||||||||||||||||||||||||||||||||||||||||||
| clone := make(map[string][]CacheValue, len(i.cache)) | ||||||||||||||||||||||||||||||||||||||||||||
| for k, v := range i.cache { | ||||||||||||||||||||||||||||||||||||||||||||
| clonedV := make([]CacheValue, len(v)) | ||||||||||||||||||||||||||||||||||||||||||||
| copy(clonedV, v) | ||||||||||||||||||||||||||||||||||||||||||||
| clone[k] = clonedV | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| return clone | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| func normalizePath(path string) string { | ||||||||||||||||||||||||||||||||||||||||||||
| cleanedPath := filepath.Clean(path) | ||||||||||||||||||||||||||||||||||||||||||||
| volumeName := filepath.VolumeName(cleanedPath) | ||||||||||||||||||||||||||||||||||||||||||||
| // make sure all volume names are uppercase | ||||||||||||||||||||||||||||||||||||||||||||
| if volumeName != "" { | ||||||||||||||||||||||||||||||||||||||||||||
| cleanedPath = strings.ToUpper(volumeName) + cleanedPath[len(volumeName):] | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| return filepath.ToSlash(cleanedPath) // prevent forward / backward slashes becoming a problem | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+103
to
+111
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainMake normalizer cross-OS and handle empty input; canonicalize Windows drive letter even on non-Windows. When the server runs on Linux but receives Windows-style paths (from the VS Code extension on Windows), filepath.VolumeName("") won’t detect the drive. Canonicalize drive letter by pattern post-ToSlash; also avoid turning "" into ".". -func normalizePath(path string) string {
- cleanedPath := filepath.Clean(path)
- volumeName := filepath.VolumeName(cleanedPath)
- // make sure all volume names are uppercase
- if volumeName != "" {
- cleanedPath = strings.ToUpper(volumeName) + cleanedPath[len(volumeName):]
- }
- return filepath.ToSlash(cleanedPath) // prevent forward / backward slashes becoming a problem
-}
+func normalizePath(p string) string {
+ if p == "" {
+ return ""
+ }
+ s := filepath.ToSlash(filepath.Clean(p))
+ // If path looks like "C:/..." (Windows-style), normalize drive letter case on all OSes.
+ if len(s) >= 2 && s[1] == ':' {
+ s = strings.ToUpper(s[:1]) + s[1:]
+ }
+ return s
+}Given the linked issue was observed on Windows 11 with a likely Linux analyzer, please confirm you see identical keys for inputs like:
If you’d like, I can add a small table-driven test for normalizePath to lock this in. 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "PWD: $(pwd)"
echo "---- find occurrences of normalizePath ----"
rg -n --hidden -S '\bfunc\s+normalizePath\b|\bnormalizePath\b' || true
echo "---- show kai_analyzer_rpc/pkg/service/cache.go if present ----"
if [ -f "kai_analyzer_rpc/pkg/service/cache.go" ]; then
sed -n '1,300p' kai_analyzer_rpc/pkg/service/cache.go || true
else
echo "File not found at kai_analyzer_rpc/pkg/service/cache.go"
echo "Searching for cache.go files..."
rg -n --hidden -S 'cache.go' || true
fi
echo "---- search repo for VolumeName, ToSlash, filepath.Clean ----"
rg -n --hidden -S 'filepath\.VolumeName|VolumeName\(|filepath\.ToSlash|ToSlash\(|filepath\.Clean' || true
echo "---- show analyzer.go if present ----"
if [ -f "kai_analyzer_rpc/pkg/service/analyzer.go" ]; then
sed -n '1,400p' kai_analyzer_rpc/pkg/service/analyzer.go || true
else
echo "analyzer.go not found at expected path; searching for analyzer.go..."
rg -n --hidden -S 'analyzer.go' || true
fi
echo "---- search for other normalization helpers (Normalize/NormalizePath/normalpath) ----"
rg -n --hidden -S '\bNormalize\b|\bNormalizePath\b|\bnormalpath\b' || true
echo "---- list files in kai_analyzer_rpc/pkg/service (if exists) ----"
ls -la kai_analyzer_rpc/pkg/service || trueLength of output: 17520 Make normalizePath cross-OS and handle empty input (kai_analyzer_rpc/pkg/service/cache.go:96-104) filepath.VolumeName is Windows-only — on non-Windows a Windows-style input like "c:\repo\..." won't have its drive uppercased and filepath.Clean("") returns "."; canonicalize after ToSlash and return "" for empty input to avoid cache-key mismatches. -func normalizePath(path string) string {
- cleanedPath := filepath.Clean(path)
- volumeName := filepath.VolumeName(cleanedPath)
- // make sure all volume names are uppercase
- if volumeName != "" {
- cleanedPath = strings.ToUpper(volumeName) + cleanedPath[len(volumeName):]
- }
- return filepath.ToSlash(cleanedPath) // prevent forward / backward slashes becoming a problem
-}
+func normalizePath(p string) string {
+ if p == "" {
+ return ""
+ }
+ s := filepath.ToSlash(filepath.Clean(p))
+ // If path looks like "C:/..." (Windows-style), normalize drive letter case on all OSes.
+ if len(s) >= 2 && s[1] == ':' {
+ s = strings.ToUpper(s[:1]) + s[1:]
+ }
+ return s
+}Saved learning used: analyzer is reported to receive workspace-formatted paths; if that invariant is guaranteed, skip this change — otherwise apply the patch and add table-driven tests for:
📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.