-
Notifications
You must be signed in to change notification settings - Fork 1.5k
fix(session): prevent repository session path collisions #913
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
base: main
Are you sure you want to change the base?
Changes from all commits
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 |
|---|---|---|
|
|
@@ -96,40 +96,60 @@ type summaryRecord struct { | |
| // SessionsDir returns the on-disk directory that holds JSONL session files | ||
| // for a given repository. It does not create the directory. | ||
| func SessionsDir(repoDir string) (string, error) { | ||
| home, err := os.UserHomeDir() | ||
| if err != nil { | ||
| return "", fmt.Errorf("resolve home dir: %w", err) | ||
| } | ||
| return filepath.Join(home, ".opencodereview", sessionSubDir, encodeRepoPath(repoDir)), nil | ||
| current, _, err := sessionDirectories(repoDir) | ||
| return current, err | ||
| } | ||
|
|
||
| // ListSessions enumerates all persisted sessions for the given repository | ||
| // directory, sorted by StartTime descending (most recent first). Missing | ||
| // directories return an empty slice with no error. | ||
| func ListSessions(repoDir string) ([]Summary, error) { | ||
| dir, err := SessionsDir(repoDir) | ||
| currentDir, legacyDir, err := sessionDirectories(repoDir) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| entries, err := os.ReadDir(dir) | ||
| if err != nil { | ||
| if os.IsNotExist(err) { | ||
| return nil, nil | ||
| summaries := make([]Summary, 0) | ||
| seenSessionIDs := make(map[string]struct{}) | ||
| readDir := func(dir string, legacy bool) error { | ||
| entries, readErr := os.ReadDir(dir) | ||
| if readErr != nil { | ||
| if os.IsNotExist(readErr) { | ||
| return nil | ||
| } | ||
| return fmt.Errorf("read sessions dir %q: %w", dir, readErr) | ||
| } | ||
| return nil, fmt.Errorf("read sessions dir %q: %w", dir, err) | ||
| } | ||
| summaries := make([]Summary, 0, len(entries)) | ||
| for _, entry := range entries { | ||
| name := entry.Name() | ||
| if entry.IsDir() || !strings.HasSuffix(name, ".jsonl") { | ||
| continue | ||
| for _, entry := range entries { | ||
| name := entry.Name() | ||
| if entry.IsDir() || !strings.HasSuffix(name, ".jsonl") { | ||
| continue | ||
| } | ||
| path := filepath.Join(dir, name) | ||
| if legacy { | ||
| matches, matchErr := sessionFileBelongsToRepo(path, repoDir) | ||
| if matchErr != nil || !matches { | ||
| continue | ||
| } | ||
| } | ||
|
Comment on lines
+127
to
+132
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.
|
||
| sessionID := strings.TrimSuffix(name, ".jsonl") | ||
| if _, seen := seenSessionIDs[sessionID]; seen { | ||
| continue | ||
| } | ||
| summary, loadErr := loadSummaryFromFile(path, sessionID, repoDir) | ||
| if loadErr != nil { | ||
| continue | ||
| } | ||
| summaries = append(summaries, *summary) | ||
| seenSessionIDs[sessionID] = struct{}{} | ||
| } | ||
| sessionID := strings.TrimSuffix(name, ".jsonl") | ||
| summary, err := loadSummaryFromFile(filepath.Join(dir, name), sessionID, repoDir) | ||
| if err != nil { | ||
| continue | ||
| return nil | ||
| } | ||
| if err := readDir(currentDir, false); err != nil { | ||
| return nil, err | ||
| } | ||
| if legacyDir != currentDir { | ||
| if err := readDir(legacyDir, true); err != nil { | ||
| return nil, err | ||
| } | ||
| summaries = append(summaries, *summary) | ||
| } | ||
|
amh1k marked this conversation as resolved.
|
||
| sort.Slice(summaries, func(i, j int) bool { | ||
| return summaries[i].StartTime.After(summaries[j].StartTime) | ||
|
|
@@ -140,7 +160,7 @@ func ListSessions(repoDir string) ([]Summary, error) { | |
| // LoadSummary loads a single session's Summary. Errors when the session | ||
| // file is missing or unreadable. | ||
| func LoadSummary(repoDir, sessionID string) (*Summary, error) { | ||
| path, err := SessionFilePath(repoDir, sessionID) | ||
| path, err := findSessionFile(repoDir, sessionID) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
@@ -149,7 +169,7 @@ func LoadSummary(repoDir, sessionID string) (*Summary, error) { | |
|
|
||
| // LoadDetail returns the summary plus per-file item records for one session. | ||
| func LoadDetail(repoDir, sessionID string) (*Summary, []ItemDetail, error) { | ||
| path, err := SessionFilePath(repoDir, sessionID) | ||
| path, err := findSessionFile(repoDir, sessionID) | ||
| if err != nil { | ||
| return nil, nil, err | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Performance regression: The original code pre-allocated slice capacity using
make([]Summary, 0, len(entries))based on the directory entry count. The new code usesmake([]Summary, 0)without capacity, which will cause multiple slice reallocations as sessions are appended. While individually small, this adds unnecessary allocations especially when listing repositories with many sessions across both directories.Suggestion: