Skip to content

Remove caching of raw_segments_with_ancestors to prevent memory leaks#2430

Open
benfdking wants to merge 3 commits intomainfrom
claude/fix-sqruff-memory-leak-rsm0R
Open

Remove caching of raw_segments_with_ancestors to prevent memory leaks#2430
benfdking wants to merge 3 commits intomainfrom
claude/fix-sqruff-memory-leak-rsm0R

Conversation

@benfdking
Copy link
Copy Markdown
Collaborator

Summary

This PR removes the caching of raw_segments_with_ancestors from the NodeData struct and converts the method to compute the result on-demand instead. This change prevents reference cycles that were causing unbounded memory growth in long-running processes like the LSP server.

Key Changes

  • Removed OnceCell field: Deleted the raw_segments_with_ancestors: OnceCell<Vec<(ErasedSegment, Vec<PathStep>)>> field from NodeData struct
  • Changed return type: Modified raw_segments_with_ancestors() method to return Vec<(ErasedSegment, Vec<PathStep>)> instead of &[(ErasedSegment, Vec<PathStep>)] to support on-demand computation
  • Removed caching logic: Replaced get_or_init() calls with direct computation of the result
  • Updated callers: Modified DepthMap::from_parent() to store the computed vector before iterating over it
  • Removed field initialization: Cleaned up initialization of the removed field in SegmentBuilder and ErasedSegment::clone()

Implementation Details

The issue was that PathStep stores an Rc clone of its ancestor segment, creating reference cycles when cached inside NodeData (which is itself behind Rc). These cycles prevented the entire syntax tree from being deallocated, causing memory to grow unboundedly in long-running processes.

By computing raw_segments_with_ancestors on-demand rather than caching it, we avoid creating persistent reference cycles while maintaining the same functionality. The performance impact is acceptable since this method is not called in hot paths.

Added detailed comments explaining why caching is intentionally avoided to prevent future regressions.

https://claude.ai/code/session_01GhEcspeHFuBffJnphrDhLJ

claude and others added 2 commits March 16, 2026 15:53
The `raw_segments_with_ancestors` method cached its result in a OnceCell
inside NodeData. Each cached PathStep stored an Rc clone of its ancestor
segment, creating a reference cycle (node -> cache -> PathStep -> node)
that prevented the entire syntax tree from being deallocated. In
long-running processes like the LSP server, this caused unbounded memory
growth on every lint operation.

Fix by removing the OnceCell cache and computing the result fresh each
call. The PathSteps are now temporary and properly dropped after use,
breaking the cycle.

https://claude.ai/code/session_01GhEcspeHFuBffJnphrDhLJ
@github-actions
Copy link
Copy Markdown
Contributor

Benchmark for d750708

Click to view benchmark
Test Base PR %
DepthMap::from_parent 51.7±0.70µs 103.5±2.26µs +100.19%
fix_complex_query 12.0±0.14ms 12.7±0.20ms +5.83%
fix_superlong 180.4±11.16ms 167.1±17.49ms -7.37%
parse_complex_query 4.3±0.10µs 4.2±0.09µs -2.33%
parse_expression_recursion 7.2±0.14µs 7.3±0.11µs +1.39%
parse_simple_query 1059.6±28.95ns 1085.5±15.35ns +2.44%

The test builds a small segment tree, calls raw_segments_with_ancestors(),
and asserts that Rc::strong_count on the root is exactly 1 afterward.

On the old code (with OnceCell caching), this test fails with
strong_count=3 — the two cached PathSteps each hold an Rc clone back
to the parent, creating a reference cycle that prevents deallocation.

https://claude.ai/code/session_01GhEcspeHFuBffJnphrDhLJ
@github-actions
Copy link
Copy Markdown
Contributor

Benchmark for 3c95ad5

Click to view benchmark
Test Base PR %
DepthMap::from_parent 53.2±1.39µs 105.6±2.41µs +98.50%
fix_complex_query 11.7±0.22ms 12.5±0.16ms +6.84%
fix_superlong 183.7±18.30ms 173.5±17.59ms -5.55%
parse_complex_query 4.2±0.06µs 4.1±0.06µs -2.38%
parse_expression_recursion 7.1±0.13µs 7.2±0.08µs +1.41%
parse_simple_query 1065.2±20.19ns 1080.1±31.64ns +1.40%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants