[ZEPPELIN-6575] Reclaim idle interpreters on the server with a per-setting timeout - #5358
Open
big-cir wants to merge 1 commit into
Open
[ZEPPELIN-6575] Reclaim idle interpreters on the server with a per-setting timeout#5358big-cir wants to merge 1 commit into
big-cir wants to merge 1 commit into
Conversation
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?
An interpreter setting cannot have its own idle timeout today, and worse, trying to give it one fails silently.
Idle reclaim is decided inside the interpreter process by
TimeoutLifecycleManager, which reads its threshold from the configuration map the server pushes over Thrift when the process starts. That map is built byZeppelinConfiguration#getCompleteConfiguration():Its key set is closed over the
ConfVarsenum, so an interpreter setting property has no slot to travel in. An operator can put the threshold property on a single interpreter's settings, it is stored, it is shown again when the form is reopened - and the process still starts with the global value. No error, no warning.So the threshold is effectively all-or-nothing across every interpreter, which does not match how they differ in cost. A Spark interpreter holding tens of gigabytes of cluster memory is worth reclaiming aggressively; a JDBC interpreter that only keeps a few connections open is usually worth keeping. Today an operator who enables idle reclaim gets one number for both.
This moves the decision to the server, which already knows the per-setting value:
ManagedInterpreterGrouprecords when the group was last used. The three hooks mirror the onesRemoteInterpreterServeralready calls in-process (interpret,getProgress,getStatus), so no new notion of activity is introduced.getStatusmatters most:RemoteScheduler.JobStatusPollercalls it while a paragraph runs, which is what keeps a long-running paragraph from having its interpreter pulled out from under it.IdleInterpreterReclaimerwalksgetAllInterpreterGroup()on a timer, resolves the threshold from the owning interpreter setting (falling back to the global property), and closes the group by reusingManagedInterpreterGroup.close(). It reads in-memory state only and never callsisAlive()/isRunning(), whose cost depends on the launcher - a socket connect with a 1s timeout for docker, an unbounded kube-apiserver round trip for k8s - and this runs over every group on a timer.start(), while the idle clock has been running since the group was created, so without this a launch slower than the threshold gets killed while coming up. Spark on YARN takes minutes to launch, so this is not hypothetical.No new configuration property, and nothing changes unless asked for. This follows the existing lifecycle manager class property, which already expresses whether idle reclaim is wanted at all. Its
NullLifecycleManagerdefault means an existing deployment sees no change whatsoever;TimeoutLifecycleManagernow also enables server-driven reclaim. Any other implementation is left untouched.ZeppelinConfiguration#getLifecycleManagerClass()had no caller before this change.An interpreter setting overrides the threshold by carrying the same property name, where
0or below means never reclaimed. This is opt-in per interpreter: a setting that carries nothing keeps following the global threshold exactly as before, so the JDBC interpreter above is still reclaimed on the global schedule until an operator marks it as exempt. What the change adds is the ability to say it at all.TimeoutLifecycleManageris kept, not replaced. It still runs in the interpreter process, because that is what shuts a process down if the Zeppelin server itself exits unexpectedly and can no longer reclaim anything. It is handed the same threshold resolved here rather than the global one, so the two sides agree instead of one of them shutting a process down on the wrong schedule. Closing a group twice cannot happen either: if the process shuts itself down it unregisters, which removes the group fromgetAllInterpreterGroup(); if the server closes the group first, the process and its scheduler are gone.That fallback is intentionally given up for one case only - a setting the operator marked as never reclaimed (
0), whose process receivesLong.MAX_VALUEand therefore will not self-terminate either.TimeoutLifecycleManagerhas no way to express "never" and would read a threshold of0as "shut down at the next check".What type of PR is it?
Feature
Todos
ManagedInterpreterGroup.close()docs/usage/interpreter/overview.mdandconf/zeppelin-site.xml.templateWhat is the Jira issue?
How should this be tested?
IdleInterpreterReclaimerTest(9 tests) covers both directions of the override, the launching guard, the no-probe guard, threshold resolution, and that the default lifecycle manager changes nothing.The two override tests fail before the change, because the per-setting value never reaches the process:
aGroupBeingLaunchedIsNotReclaimedfails without the launching guard, andscanNeverProbesTheInterpreterProcessfails if the scan is written withisAlive()/isRunning().Local runs, all passing:
org.apache.zeppelin.interpreter.**(19 classes, includes recovery and launcher tests)notebook,rest,service,socket,server,notebook.repo(39 classes, 318 tests)TimeoutLifecycleManagerTest(existing idle reclaim behaviour)./mvnw clean org.apache.rat:apache-rat-plugin:check -PratUnapproved: 0Manual steps on a running server. This uses two of the lightweight built-in interpreters,
md(markdown) andsh(shell), so that two interpreters run side by side under one server and one global threshold:zeppelin-site.xmlset the lifecycle manager class toTimeoutLifecycleManager, the global threshold to10s, and the check interval to5s.mdinterpreter setting only, add the threshold property with the value0to mark it as never reclaimed. Leave theshsetting untouched so that it follows the global 10s.%mdparagraph and one%shparagraph, then leave the note idle.Each interpreter setting gets its own process, and each process logs the threshold it was handed:
Before this change both processes were handed the global
10000and both were shut down after 10s of idle time; the0on themdsetting had no effect at all. Now onlyshis reclaimed, and the server log records why:psconfirmed theshprocess was gone about 15s after its last use, while themdprocess was still running after 33s of idle time. Both processes kept the lifecycle manager class the operator configured; only the threshold they received differed.Not verified locally, left to CI and to deployments that have the runtimes: the docker, k8s and yarn launchers. The reclaim path does not call into a launcher - it neither probes nor launches, only closes - so the exposure is limited to
close(), which the existing restart endpoint already uses.Screenshots (if appropriate)
N/A
Questions:
apache-rat-plugin:checkreports no unapproved files.NullLifecycleManagernothing is scheduled and no configuration is overridden for the interpreter process, so an untouched deployment behaves exactly as before.docs/usage/interpreter/overview.mdgains a "Per interpreter idle threshold" section, and the descriptions inconf/zeppelin-site.xml.templateare extended. No values in the template were changed.