-
Notifications
You must be signed in to change notification settings - Fork 2.8k
[ZEPPELIN-6574] Add read-only REST API for interpreter process status #5403
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one or more | ||
| * contributor license agreements. See the NOTICE file distributed with | ||
| * this work for additional information regarding copyright ownership. | ||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | ||
| * (the "License"); you may not use this file except in compliance with | ||
| * the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package org.apache.zeppelin.interpreter; | ||
|
|
||
| import org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcess; | ||
|
|
||
| /** | ||
| * Point-in-time status snapshot of a single interpreter process as seen by the Zeppelin server. | ||
| * Built purely from in-memory server state without contacting the process, so {@code started} | ||
| * reflects whether a process handle exists, not whether the process is currently reachable. | ||
| * Reachability is intentionally out of scope here to keep the read path non-blocking. | ||
| */ | ||
| public class InterpreterProcessStatus { | ||
| private final String settingId; | ||
| private final String settingName; | ||
| private final String groupId; | ||
| private final int numSessions; | ||
| private final boolean started; | ||
| private String host; | ||
| private int port = -1; | ||
| private String startTime; | ||
| private long uptimeSeconds; | ||
| private String errorMessage; | ||
|
|
||
| public InterpreterProcessStatus(ManagedInterpreterGroup group) { | ||
| InterpreterSetting setting = group.getInterpreterSetting(); | ||
| this.settingId = setting.getId(); | ||
| this.settingName = setting.getName(); | ||
| this.groupId = group.getId(); | ||
| this.numSessions = group.getSessionNum(); | ||
| RemoteInterpreterProcess process = group.getInterpreterProcess(); | ||
| this.started = process != null; | ||
| if (started) { | ||
| this.host = process.getHost(); | ||
| this.port = process.getPort(); | ||
|
Comment on lines
+49
to
+50
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Most of the values this snapshot reads are non-volatile and are written on a different thread from the one reading them:
All of this predates the PR, so it is not something this change introduced. I mention it because this API is the first place that state becomes a documented contract, so reporting a stale value now has a visible consequence. It also feeds directly into the phase decision if you take the port-based approach above. A few |
||
| this.startTime = process.getStartTime(); | ||
| this.uptimeSeconds = (System.currentTimeMillis() - process.getStartTimeMs()) / 1000; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In those cases The existing One small thing alongside it: |
||
| this.errorMessage = process.getErrorMessage(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The PR describes "No remote probe. Built from in-memory server state only, so a stuck interpreter cannot block the call" as the core contract of this endpoint, but this line may not hold to it. Two separate things seem to be going on. First,
Second, on the default launcher this value may not be an error signal at all. if (!StringUtils.isBlank(processOutput.getProcessExecutionOutput())) {
return processOutput.getProcessExecutionOutput();
}and Dropping the field would remove both the blocking call and the payload concern, and would make the contract in the PR description true on every launcher. Adding diagnostics later, alongside the bounded probe in 6576, may be a more natural fit. What do you think? If you would rather address it here, there is another option. The failure information is already pushed onto the process object:
The cost of finding out has therefore already been paid by a watcher, and a reader only needs to read a field. Holding that value in the base class behind a final accessor that a launcher cannot override would let the snapshot take the cheap path while That direction has limits worth stating too. Docker records nothing at all (no watcher, no |
||
| } | ||
| } | ||
|
|
||
| public String getSettingId() { | ||
| return settingId; | ||
| } | ||
|
|
||
| public String getSettingName() { | ||
| return settingName; | ||
| } | ||
|
|
||
| public String getGroupId() { | ||
| return groupId; | ||
| } | ||
|
|
||
| public int getNumSessions() { | ||
| return numSessions; | ||
| } | ||
|
|
||
| public boolean isStarted() { | ||
| return started; | ||
| } | ||
|
|
||
| public String getHost() { | ||
| return host; | ||
| } | ||
|
|
||
| public int getPort() { | ||
| return port; | ||
| } | ||
|
|
||
| public String getStartTime() { | ||
| return startTime; | ||
| } | ||
|
|
||
| public long getUptimeSeconds() { | ||
| return uptimeSeconds; | ||
| } | ||
|
|
||
| public String getErrorMessage() { | ||
| return errorMessage; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ManagedInterpreterGroup.getOrCreateInterpreterProcess()assignsremoteInterpreterProcess = createInterpreterProcess(...)beforestart(), and the reader does not takeinterpreterProcessCreationLock. A call during a launch can therefore observe a handle whose host and port are still at their initial values (null/-1atRemoteInterpreterManagedProcess:35-36).A single
startedboolean does not let a consumer tell that window apart from a fully started process, so you may want a second field. ConvenientlyManagedInterpreterGroup.isLaunchingInterpreterProcess()already exists, or the state could be derived from whetherporthas been filled in. The latter looks more robust for separating "handle created / awaiting registration / registered" at no extra cost, though you may prefer the simplicity of one boolean, so I will leave it as a matter of taste.Combined with the uptime in the note above, this would also make "stuck awaiting registration" visible on its own, which catches a fair amount without any remote probe.