[ZEPPELIN-6579] Make notebook tree reload safe for concurrent note operations - #5357
Open
big-cir wants to merge 1 commit into
Open
[ZEPPELIN-6579] Make notebook tree reload safe for concurrent note operations#5357big-cir wants to merge 1 commit into
big-cir wants to merge 1 commit into
Conversation
jongyoul
approved these changes
Jul 30, 2026
Member
|
Thanks for fixing the mixed-generation lookup failure. I narrowed the title to match this focused scope: atomically publishing one fully built notebook-tree state during reload. Please address writer-side coordination in a follow-up PR: concurrent reloads, and reload racing with add/save/move/remove. That follow-up should include deterministic latch-based tests and account for the overlap with #5325. The current approval remains for this focused fix. |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
What is this PR for?
NoteManagerlocates a note through two separate pieces of state:notesInfomaps a note id to its path, androotholds the folder tree that the path is walked against. A lookup uses both in sequence, so the two have to agree.reloadNotes()replaced them one at a time:Neither field is
volatileand nothing is held while they are swapped, so a concurrentprocessNote()can observe a mapping and a tree that belong to different generations:notesInfo.containsKey(noteId)passesThe guard in
processNote()only checksnotesInfo, so it passes and the failure surfaces one line later ingetNoteNode():IOExceptionis not mapped to a specific status, soWebApplicationExceptionMapperturns it into HTTP 500 for a note that was never removed. Everything that goes throughprocessNote()is affected: reading a note, updating a paragraph, creating, deleting and moving notes, and listing the notebook.This PR holds the tree, the trash folder and the mapping in one immutable
NoteTreeand publishes it with a singlevolatilewrite.buildNoteTree()fills the new tree locally and returns it; only then is it assigned. The tree-walking helpers (getNoteNode,getFolder,getOrCreateFolder,isNotePathAvailable) take the tree as a parameter, and callers that need both pieces of state read the reference once, so a lookup resolves the mapping and the tree against the same generation. Those helpers arestaticso that the compiler prevents them from reaching back to the field.Scope and related issues
#5325 (
[ZEPPELIN-5858]) is open against the same class and restructuresremoveNote,moveNoteandmoveFolderwithsynchronized (this). It targets a different race (two mutators duplicating a note) and its monitor does not coverreloadNotes(), so neither change subsumes the other. Whichever merges second will need a rebase.What type of PR is it?
Bug Fix
Todos
buildNoteTree()before publishing themNoteTreepublished through a singlevolatilewriteWhat is the Jira issue?
How should this be tested?
New test
NoteManagerTest#testConcurrentReloadAndProcessNote: it saves 50 notes, then runsreloadNotes()in a loop on one thread while four threads keep callingprocessNote()for every note, and asserts that no lookup fails or returns nothing.Result with the fix:
Tests run: 7, Failures: 0, Errors: 0.Reverting only the production change makes the new test fail on every reader thread with
java.io.IOException: Can not find note: /prod/note_0thrown fromNoteManager.getNoteNodeviaNoteManager.processNote, which is the stack from the ticket; with the fix it passes.Also run, to cover the callers of the reload path:
./mvnw package -pl zeppelin-server --am \ -Dtest='NotebookTest#testReloadAllNotes+testReloadAndSetInterpreter' -DfailIfNoTests=falseResult:
Tests run: 2, Failures: 0, Errors: 0.Not verified locally: the full
NotebookTestandNotebookServerTestclasses, which start real remote interpreter processes and time out in my environment, andNotebookRepoSyncTest. Those are left to CI.Screenshots (if appropriate)
N/A
Questions: