Skip to content

[ZEPPELIN-6693] Drive NotebookServer heartbeat scheduler shutdown from ZeppelinServer lifecycle - #5499

Merged
tbonelee merged 1 commit into
apache:masterfrom
JangAyeon:ZEPPELIN-6693
Sep 25, 2026
Merged

tbonelee merged 1 commit into
apache:masterfrom
JangAyeon:ZEPPELIN-6693

Conversation

@JangAyeon

@JangAyeon JangAyeon commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

What is this PR for?

ZEPPELIN-6092 added a websocket heartbeat scheduler to NotebookServer, torn down by a dedicated JVM shutdown hook that the class registered for itself. That hook only covers the JVM-exit path:

  • When the server is closed inside a running JVM (e.g. MiniZeppelinServer.shutDown() calling zepServer.close()), the hook never runs, so repeated start/stop cycles can leave a scheduler and a hook behind per instance.
  • On SIGTERM, ZeppelinServer's own shutdown hook and this one run in parallel for the same event.

This PR stops the scheduler from ZeppelinServer#shutdown, which already covers both paths (the JVM shutdown hook and close()), and removes the dedicated hook together with its removeShutdownHook / IllegalStateException handling and self-reference guard. The scheduler is stopped after Jetty, so no new connection can restart it.

What type of PR is it?

Improvement

Todos

  • - Stop the heartbeat scheduler from ZeppelinServer#shutdown
  • - Remove the dedicated shutdown hook from NotebookServer
  • - Add a test that covers the ZeppelinServer#shutdown path
  • - Add tests for repeated start/stop cycles

What is the Jira issue?

ZEPPELIN-6693

How should this be tested?

  • NotebookServerHeartbeatTest#zeppelinServerShutdownStopsHeartbeatScheduler: starts a MiniZeppelinServer, starts the heartbeat scheduler, calls shutDown(), and checks the scheduler is shut down. Fails if the stopHeartbeatScheduler() call in ZeppelinServer#shutdown is removed.
  • NotebookServerHeartbeatTest#stopHeartbeatSchedulerAllowsRepeatedStartStopCycles: starts and stops the scheduler three times and checks that each scheduler is shut down and a new one can start.
  • NotebookServerHeartbeatTest#stopHeartbeatSchedulerIsSafeWhenNeverStarted: stopping without a prior start, twice, does not throw.
  • Existing NotebookServerHeartbeatTest cases still pass.

Questions:

  • Does the license files need to update? No
  • Is there breaking changes for older versions? No
  • Does this needs documentation? No

@HwangRock HwangRock left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on my issue! I confirmed the feature works 👍

}

@Test
void stopHeartbeatSchedulerAllowsRepeatedStartStopCycles() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests look good!

Right now the tests all call stopHeartbeatScheduler directly,
so they'd still pass even if the ZeppelinServer#shutdown call got removed.
The actual fix is that shutdown drives the teardown, and that path isn't covered.

Might be worth adding one that goes through the real shutdown path. Spin up MiniZeppelinServer, open a connection to start the heartbeat, shut down and check the scheduler's gone.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. The two new tests also pass against the previous shutdown-hook code, so they don't pin down what this PR changes. There's no need to open a connection: calling startHeartbeatScheduler() directly, then checking scheduler.isShutdown() after MiniZeppelinServer.shutDown(), is enough.

Comment on lines +293 to +296
* Stops the websocket heartbeat scheduler, if running. Called by ZeppelinServer#shutdown so
* the scheduler follows the server lifecycle, both on JVM shutdown and when the server is
* closed inside a running JVM (for example in tests). Safe to call multiple times and safe
* to call when the scheduler was never started; a later connection starts it again.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The caller and the reason for this change are in the commit message, so the javadoc could keep just this method's contract.

Suggested change
* Stops the websocket heartbeat scheduler, if running. Called by ZeppelinServer#shutdown so
* the scheduler follows the server lifecycle, both on JVM shutdown and when the server is
* closed inside a running JVM (for example in tests). Safe to call multiple times and safe
* to call when the scheduler was never started; a later connection starts it again.
* Stops the websocket heartbeat scheduler, if running. Safe to call multiple times and safe
* to call when the scheduler was never started; a later connection starts it again.

}

@Test
void stopHeartbeatSchedulerAllowsRepeatedStartStopCycles() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. The two new tests also pass against the previous shutdown-hook code, so they don't pin down what this PR changes. There's no need to open a connection: calling startHeartbeatScheduler() directly, then checking scheduler.isShutdown() after MiniZeppelinServer.shutDown(), is enough.

…m ZeppelinServer lifecycle

The heartbeat scheduler was stopped by its own JVM shutdown hook, which never ran when the server was closed inside a running JVM (e.g. in tests), so schedulers and hooks piled up across start/stop cycles.

ZeppelinServer#shutdown now calls stopHeartbeatScheduler() after Jetty stops, covering both in-JVM close and JVM exit. The dedicated hook is removed. Add a test that checks the scheduler is stopped via MiniZeppelinServer.shutDown().
@JangAyeon

Copy link
Copy Markdown
Contributor Author

Thanks @HwangRock @tbonelee for the review! I've addressed the comments and amended them into the existing commit (force-pushed).

  • Test for the shutdown path: Added zeppelinServerShutdownStopsHeartbeatScheduler, which starts a MiniZeppelinServer, calls startHeartbeatScheduler() directly, then checks scheduler.isShutdown() after MiniZeppelinServer.shutDown(). I confirmed it fails when the stopHeartbeatScheduler() call in ZeppelinServer#shutdown is removed, so it now pins down what this PR changes.
  • Javadoc: Trimmed stopHeartbeatScheduler() to its contract as suggested, and moved the caller and rationale into the commit message.

I also updated the PR description to reflect the new test.

@tbonelee
tbonelee merged commit 24968cd into apache:master Sep 25, 2026
24 checks passed
@tbonelee

Copy link
Copy Markdown
Contributor

Merged into master

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.

3 participants