-
Notifications
You must be signed in to change notification settings - Fork 531
dont grow events unbounded when persisting #2560
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
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
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.
🧩 Analysis chain
Critical: Slice reslicing causes memory leak.
The slice reslicing operation
pe.Events[len(pe.Events)-numPersist:]creates a new slice header that shares the same underlying array. The old events at the beginning of the array remain in memory because the slice still references the underlying array, preventing garbage collection. Over time, this causes unbounded memory growth—the opposite of the PR's intent.To fix this, allocate a new slice and copy the elements:
pe.Events = append(pe.Events, &event) if len(pe.Events) > numPersist { - pe.Events = pe.Events[len(pe.Events)-numPersist:] + newEvents := make([]*WaveEvent, numPersist) + copy(newEvents, pe.Events[len(pe.Events)-numPersist:]) + pe.Events = newEvents }Additional concern: The persistence window size varies based on each event's
Persistfield value. If different events for the same (Event, Scope) key have differentPersistvalues, the window size will change unpredictably. Consider whether this is intentional or if the window size should be fixed per key.I need to verify the memory leak claim and the proposed fix. Let me examine the actual code context.
Length of output: 603
🏁 Script executed:
Length of output: 207
🏁 Script executed:
Length of output: 71
🏁 Script executed:
Length of output: 0
🏁 Script executed:
Length of output: 192
I need to verify the Go slice behavior to confirm the memory leak claim.
💡 Result:
Short answer
Why it happens
How to fix / avoid it
c := make([]byte, len(b)); copy(c, b)
(or use append to allocate-and-copy). [1]
copy(a[i:], a[j:]); for k := len(a)-j+i; k < len(a); k++ { a[k] = nil }; a = a[:len(a)-j+i]. [3]
References
Critical: Slice reslicing causes memory leak—memory leak claim confirmed.
Reslicing a slice keeps a pointer to its entire underlying array, so the GC cannot free parts of that array while any slice points into it. The code at line 222 reslices
pe.Eventsbut the underlying array continues to hold references to old events, preventing garbage collection. This contradicts the PR's intent to prevent unbounded growth.The proposed fix is correct. Copy the needed data to a new slice so it points at a freshly allocated, smaller array:
pe.Events = append(pe.Events, &event) if len(pe.Events) > numPersist { - pe.Events = pe.Events[len(pe.Events)-numPersist:] + newEvents := make([]*WaveEvent, numPersist) + copy(newEvents, pe.Events[len(pe.Events)-numPersist:]) + pe.Events = newEvents }Additional concern: The persistence window size varies based on each event's
Persistfield. Different events for the same (Event, Scope) key will have different window sizes if theirPersistvalues differ, making the window size unpredictable. Clarify whether this behavior is intentional or if the window should be fixed per key.📝 Committable suggestion
🤖 Prompt for AI Agents
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.
common false positive for go code analysis. because we are constantly appending, eventually when we hit the capacity cap the append will cause go to reallocate the backing array freeing the previously orphaned elements. the unmodified code is actually quite efficient and correct.
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.
@sawka, you're absolutely right—I apologize for the false positive.
I misanalyzed the slice behavior. When you reslice
pe.Events[len(pe.Events)-numPersist:], the resulting slice has both length and capacity equal tonumPersist(since it's sliced from the end of the underlying array). On the very nextappend, sincelen == cap, Go will allocate a fresh backing array, copy the elements, and the old array with the orphaned elements becomes unreachable for GC.This is indeed an efficient pattern—you're letting Go's append reallocation mechanism handle the cleanup naturally. Thank you for the clarification!
✏️ Learnings added