-
Notifications
You must be signed in to change notification settings - Fork 624
[LIVY-654] Ability to validate string before creating state/kind of session/statement #220
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 |
|---|---|---|
|
|
@@ -45,6 +45,12 @@ object Kind { | |
| case "sql" => SQL | ||
| case other => throw new IllegalArgumentException(s"Invalid kind: $other") | ||
| } | ||
|
|
||
| val kinds: Seq[Kind] = Seq(Spark, PySpark, SparkR, Shared, SQL) | ||
|
|
||
| def isValid(kind: String): Boolean = { | ||
|
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. Where did this get called?
Contributor
Author
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. this is public API to use as I described in the description
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. Then how do users use this public api?
Contributor
Author
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. to verify that we can create StateSession/Kind object from a string without additional logic for catching exceptions.
Contributor
Author
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. you need to add livy-core dependency in your project if you are asking about it
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. Why do you need to create StateSession or Kind object?
Contributor
Author
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. to simply use it in the code instead of hardcoding string values like "idle", sometimes I need to check session state or wait for state, for example, create a session and wait for status "idle" to be able to run statement. I don't want to use string variables everywhere. I need an enumeration. I can create my own in my code which totally duplicates Livy enum but better to use it from Livy. it is the typical case, isn't it?
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. Yes, that's the case. But from my point I never expect to expose this to users
Contributor
Author
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. hmm, having a method to check if the object is a part of the enum is common practice for Scala, it can simplify using of your enum to don't force user to catch exception or write own method to check if object exists in enum values (if exist, we don't have it now in Kind.scala, SessionState.scala that's why I have added sequences of values in these classes) |
||
| kinds.map(_.name).contains(kind) | ||
| } | ||
| } | ||
|
|
||
| class SessionKindModule extends SimpleModule("SessionKind") { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,33 +30,33 @@ class FinishedSessionState( | |
| object SessionState { | ||
|
|
||
| def apply(s: String): SessionState = s match { | ||
| case "not_started" => NotStarted | ||
| case "starting" => Starting | ||
| case "recovering" => Recovering | ||
| case "idle" => Idle | ||
| case "running" => Running | ||
| case "busy" => Busy | ||
| case "shutting_down" => ShuttingDown | ||
| case "not_started" => NotStarted() | ||
| case "starting" => Starting() | ||
| case "recovering" => Recovering() | ||
| case "idle" => Idle() | ||
| case "running" => Running() | ||
| case "busy" => Busy() | ||
| case "shutting_down" => ShuttingDown() | ||
| case "error" => Error() | ||
| case "dead" => Dead() | ||
| case "killed" => Killed() | ||
| case "success" => Success() | ||
| case _ => throw new IllegalArgumentException(s"Illegal session state: $s") | ||
| } | ||
|
|
||
| object NotStarted extends SessionState("not_started", true) | ||
| case class NotStarted() extends SessionState("not_started", true) | ||
|
|
||
| object Starting extends SessionState("starting", true) | ||
| case class Starting() extends SessionState("starting", true) | ||
|
|
||
| object Recovering extends SessionState("recovering", true) | ||
| case class Recovering() extends SessionState("recovering", true) | ||
|
|
||
| object Idle extends SessionState("idle", true) | ||
| case class Idle() extends SessionState("idle", true) | ||
|
|
||
| object Running extends SessionState("running", true) | ||
| case class Running() extends SessionState("running", true) | ||
|
|
||
| object Busy extends SessionState("busy", true) | ||
| case class Busy() extends SessionState("busy", true) | ||
|
|
||
| object ShuttingDown extends SessionState("shutting_down", false) | ||
| case class ShuttingDown() extends SessionState("shutting_down", false) | ||
|
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. What's purpose of changes here to switch to case class?
Contributor
Author
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. To unify access to enumeration type and to be able to create sequence of all states
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 changes here will create many small
Contributor
Author
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. we need to use either object or class to be able to create a sequence of states. as far as I see, you changed case class to object for FinishedSessionState classes to fix the problem with time variable initialization, so we can't use singleton everywhere. therefore, I think this is not critical to use case class instead of object for other states. |
||
|
|
||
| case class Killed(override val time: Long = System.nanoTime()) extends | ||
| FinishedSessionState("killed", false, time) | ||
|
|
@@ -69,4 +69,11 @@ object SessionState { | |
|
|
||
| case class Success(override val time: Long = System.nanoTime()) extends | ||
| FinishedSessionState("success", false, time) | ||
|
|
||
| val states: Seq[SessionState] = Seq(NotStarted(), Starting(), Recovering(), Idle(), Running(), | ||
| Busy(), ShuttingDown(), Killed(), Error(), Dead(), Success()) | ||
|
|
||
| def isValid(state: String): Boolean = { | ||
| states.map(_.state).contains(state) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,7 @@ | |
| import java.util.*; | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonValue; | ||
| import org.apache.commons.lang.StringUtils; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
|
|
@@ -73,13 +74,18 @@ static void put(StatementState key, | |
| PREDECESSORS = Collections.unmodifiableMap(predecessors); | ||
| } | ||
|
|
||
| static boolean isValid(StatementState from, StatementState to) { | ||
| static boolean isAllowed(StatementState from, StatementState to) { | ||
| return PREDECESSORS.get(to).contains(from); | ||
| } | ||
|
|
||
| public static boolean isValid(String state) { | ||
| return Arrays.stream(values()) | ||
| .map(x -> StringUtils.capitalize(x.state)).anyMatch(state::equals); | ||
|
Contributor
Author
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. Can we change naming (upper case instead of Pascal case) for enum values. For example,
|
||
| } | ||
|
|
||
| static void validate(StatementState from, StatementState to) { | ||
| LOG.debug("{} -> {}", from, to); | ||
| if (!isValid(from, to)) { | ||
| if (!isAllowed(from, to)) { | ||
| throw new IllegalStateException("Illegal Transition: " + from + " -> " + to); | ||
| } | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
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.
Can we also move object SQL and other kinds inside object Kind to be able to use:
import org.apache.livy.sessions.Kind._instead ofimport org.apache.livy.sessions.{Kind, PySpark, SessionState, Spark, SparkR, SQL}It looks more convenient.
Is it critical for backward compatibility?
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.
Is better to use
valueseverywhere (instead ofkinds/states)?