Skip to content

Drop each tree from both RPC peers once parseProject's caller is done with it - #8969

Draft
jkschneider wants to merge 1 commit into
evict-apifrom
parse-project-evict
Draft

jkschneider wants to merge 1 commit into
evict-apifrom
parse-project-evict

Conversation

@jkschneider

@jkschneider jkschneider commented Sep 27, 2026 •

Copy link
Copy Markdown
Member

Stacked on #8512, which keeps interned refs alive across source files. Review that first; this PR's diff is only the last commit.

The Python, Go, C# and JavaScript parseProject streams fetch each tree with getObject, which records it in RewriteRpc's localObjects and remoteObjects, and nothing ever evicts it. The peer keeps its own copy as well. A consumer that handles each tree and lets go of it still ends up holding the whole project, twice over, until the RPC process is shut down.

Measured with a consumer that writes each tree to disk and prints it, on googleapis/python-aiplatform (5,044 Python files) with a 3 GB heap: every compilation unit fetched so far stayed live, and the heap was full and thrashing by file 1,558.

Evict the previous item, not the current one

Each spliterator now evicts item N when the caller asks for item N+1, and the last one when it asks past the end. A sequential consumer is finished with a tree by the time it asks for the next.

Evicting on arrival looked equivalent and isn't. Printing a Python, Go, C# or JavaScript tree goes through RewriteRpc.print, which puts the tree back into localObjects so the peer can fetch it. With the tree still in remoteObjects that fetch is a NO_CHANGE diff. Evicted first, the whole tree is sent back to a peer that has just dropped its copy, and afterwards it sits in both maps again with nothing to evict it. That version held every tree on the same project.

Eviction keeps refs, per #8512, so later files still cite the types earlier ones sent.

Result

The same python-aiplatform build completes all 5,044 files at 3 GB, with 5 compilation units live at any time instead of every one fetched so far. What still grows is the ref table: 4.29M refs by the end, mostly method and variable types that only one file uses. That is #8512's trade-off, and it fits comfortably here.

On Go, this is necessary but not sufficient for milvus-io/milvus; the rest is #8970, which stops the Go type mapper creating a new type at every use.

… with it

The Python, Go, C# and JavaScript parseProject streams fetched each tree with
getObject, which records it in RewriteRpc's localObjects and remoteObjects, and never
evicted it. The peer kept its copy too. A consumer that writes each tree and lets go of
it, like an LST writer, still ended up holding the whole project: building
googleapis/python-aiplatform at -Xmx3g, every compilation unit fetched so far stayed
live and the heap filled after about 1,550 of 5,044 files.

Each item is now evicted when the caller asks for the next one. Evicting on arrival
isn't enough: the writer prints each tree, and printing a Python tree registers it with
RewriteRpc again and round-trips it through the peer, which then has to be sent the
whole tree back. Refs are kept, so later files still cite the types earlier ones sent.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

1 participant