-
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
20d9107
679ed08
f9ed7d9
e09eb13
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,111 @@ | ||
| /* | ||
| * 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. | ||
| * | ||
| * <p>Every value below must be readable without leaving the JVM. In particular do not call | ||
| * {@code isRunning()}, {@code isAlive()} or {@code getErrorMessage()} on the process from here: | ||
| * those are failure-path diagnostics that contact the container runtime on some launchers, so | ||
| * calling them would let a slow or unreachable runtime block this endpoint. | ||
| * | ||
| * <p>{@code started} and {@code launching} are read one after the other rather than under the | ||
| * lock that guards a launch, so this is a best-effort view of a group that is starting up: the | ||
| * pair can straddle the moment a launch finishes. What it does buy is that the window in which | ||
| * a handle carries no {@code host} or {@code port} yet is reported as such instead of looking | ||
| * like a fully started process. | ||
| */ | ||
| public class InterpreterProcessStatus { | ||
| private final String settingId; | ||
| private final String settingName; | ||
| private final String groupId; | ||
| private final int numSessions; | ||
| private final boolean launching; | ||
| private final boolean started; | ||
| private String host; | ||
| private int port = -1; | ||
| private String startTime; | ||
| private long attachedForSeconds; | ||
|
|
||
| public InterpreterProcessStatus(ManagedInterpreterGroup group) { | ||
| InterpreterSetting setting = group.getInterpreterSetting(); | ||
| this.settingId = setting.getId(); | ||
| this.settingName = setting.getName(); | ||
| this.groupId = group.getId(); | ||
| this.numSessions = group.getSessionNum(); | ||
| this.launching = group.isLaunchingInterpreterProcess(); | ||
| RemoteInterpreterProcess process = group.getInterpreterProcess(); | ||
| this.started = process != null; | ||
| if (started) { | ||
| this.host = process.getHost(); | ||
| this.port = process.getPort(); | ||
|
Comment on lines
+61
to
+62
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.attachedForSeconds = (System.currentTimeMillis() - process.getStartTimeMs()) / 1000; | ||
| } | ||
| } | ||
|
|
||
| public String getSettingId() { | ||
| return settingId; | ||
| } | ||
|
|
||
| public String getSettingName() { | ||
| return settingName; | ||
| } | ||
|
|
||
| public String getGroupId() { | ||
| return groupId; | ||
| } | ||
|
|
||
| public int getNumSessions() { | ||
| return numSessions; | ||
| } | ||
|
|
||
| /** | ||
| * @return whether a process is currently being launched for this group, in which case | ||
| * {@code host} and {@code port} may not be filled in yet even when {@code started} | ||
| */ | ||
| public boolean isLaunching() { | ||
| return launching; | ||
| } | ||
|
|
||
| public boolean isStarted() { | ||
| return started; | ||
| } | ||
|
|
||
| public String getHost() { | ||
| return host; | ||
| } | ||
|
|
||
| public int getPort() { | ||
| return port; | ||
| } | ||
|
|
||
| public String getStartTime() { | ||
| return startTime; | ||
| } | ||
|
|
||
| public long getAttachedForSeconds() { | ||
| return attachedForSeconds; | ||
| } | ||
| } | ||
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.