-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
[Serve] Track user-configured options in Serve deployments #28313
[Serve] Track user-configured options in Serve deployments #28313
Conversation
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
DEFAULT.VALUE
as default config values for deploymentsSigned-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
init_kwargs: Default[Dict[Any, Any]] = DEFAULT.VALUE, | ||
route_prefix: Default[Union[str, None]] = DEFAULT.VALUE, | ||
ray_actor_options: Default[Dict] = DEFAULT.VALUE, | ||
user_config: Default[Optional[Any]] = DEFAULT.VALUE, |
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.
Please correct me if i am wrong, we are having DEFAULT.VALUE
is 1
, what does it mean for setting every attribute to 1
here?
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.
It’s not quite setting every attribute to 1 here. DEFAULT.VALUE
is a Python enum element with an arbitrary value (in this case 1). It’s not an alias for 1. It could be set to any other arbitrary value (e.g. 2, “hello”, etc.) and the code here would still be the same.
I’m using DEFAULT.VALUE
as the default because the user will never pass that in as a value for a deployment option (since it’s meaningless to the user and it would require them to import the enum from the Serve codebase). So in the decorator body, if any attributes are set to DEFAULT.VALUE
, we know that the user didn’t set them. That lets us populate user_configured_options
correctly.
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.
In [3]: from enum import Enum, auto
In [4]: class DEFAULT(Enum):
...: VALUE = auto()
...:
In [5]: DEFAULT.VALUE
Out[5]: <DEFAULT.VALUE: 1>
In [6]: DEFAULT.VALUE == 1
Out[6]: False
In [7]: DEFAULT.VALUE == DEFAULT.VALUE
Out[7]: True
python/ray/serve/BUILD
Outdated
@@ -49,6 +49,14 @@ py_test( | |||
deps = [":serve_lib"], | |||
) | |||
|
|||
py_test( | |||
name = "test_deployment", |
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.
more descriptive naming please
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.
I renamed it to test_deployment_class
. Let me know if you have other suggestions. I'm trying to differentiate these tests, which are about the Deployment
class and how it manipulates data, and the Deployment
objects that actually run on the Ray cluster.
@@ -88,7 +88,7 @@ def default(self, obj): | |||
if isinstance(obj, DeploymentSchema): | |||
return { | |||
DAGNODE_TYPE_KEY: "DeploymentSchema", | |||
"schema": obj.dict(), | |||
"schema": obj.dict(exclude_defaults=True), |
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.
add a comment on why do we need this flag
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.
Sounds good, I added a comment.
python/ray/serve/config.py
Outdated
ignore_none: When True, any valid keywords with value None | ||
are ignored, and their values stay default. Invalid keywords | ||
still raise a TypeError. | ||
ignore_default: When True, any valid keywords with value |
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.
this flag appear to be unused
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.
Good catch– I removed the argument from the docstring.
src/ray/protobuf/serve.proto
Outdated
@@ -88,6 +88,8 @@ message DeploymentConfig { | |||
AutoscalingConfig autoscaling_config = 10; | |||
|
|||
string version = 11; | |||
|
|||
repeated string user_configured_options = 12; |
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.
why is this repeated string
type?
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.
In the protobuf, user_configured_options
is a list of strings. Each string is a deployment option that was manually tuned by the user.
I used repeated string
because it seemed to best match a list of strings. Is there another datatype I should use?
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.
Each string is a deployment options that was manually tuned by the user.
Ahhh these are options keys. not the value. can you change the variable name so it implies that?
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.
Good idea, I renamed user_configured_options
to user_configured_option_names
.
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
@@ -88,7 +88,10 @@ def default(self, obj): | |||
if isinstance(obj, DeploymentSchema): | |||
return { | |||
DAGNODE_TYPE_KEY: "DeploymentSchema", | |||
"schema": obj.dict(), | |||
# The schema's default values are Python enums that aren't | |||
# JSON-serializable. exclude_defaults omits these, so the |
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.
# JSON-serializable. exclude_defaults omits these, so the | |
# JSON-serializable by design. exclude_defaults omits these, so the |
python/ray/serve/deployment.py
Outdated
_internal: If True, this function: | ||
1. Won't log deprecation warnings | ||
2. Won't update this deployment's config's | ||
user_configured_options. | ||
Should only be True when used internally by Serve. | ||
Should be False when called by users. |
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.
make these complete sentences in one section please.
# Type alias: objects that can be DEFAULT.VALUE have type Default[T] | ||
T = TypeVar("T") | ||
Default = Union[DEFAULT, T] |
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.
This is so cool, learned something new :D.
from typing import TypeVar, Union
from enum import Enum, auto
class DEFAULT(Enum):
VALUE = auto()
T = TypeVar("T")
Default = Union[DEFAULT, T]
def func(name: Default[str] = DEFAULT.VALUE):
print(name)
func()
func("a")
func(1)
$ mypy app.py
app.py:15: error: Argument 1 to "func" has incompatible type "int"; expected "Union[DEFAULT, str]"
Found 1 error in 1 file (checked 1 source file)
python/ray/serve/scripts.py
Outdated
# Remove extraneous newline | ||
config_str = config_str[:-1] |
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 this really necessary? if so, please use config.rstrip("\n")
so it's more readable
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.
Without that line, the file ends with two newlines. Ideally, it should end with a single newline.
I've replaced that snippet with:
# Ensure file ends with only one newline
config_str = config_str.rstrip("\n") + "\n"
src/ray/protobuf/serve.proto
Outdated
@@ -88,6 +88,8 @@ message DeploymentConfig { | |||
AutoscalingConfig autoscaling_config = 10; | |||
|
|||
string version = 11; | |||
|
|||
repeated string user_configured_options = 12; |
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.
Each string is a deployment options that was manually tuned by the user.
Ahhh these are options keys. not the value. can you change the variable name so it implies that?
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
# Create list of all user-configured options from keyword args | ||
user_configured_option_names = [ | ||
option | ||
for option, value in locals().items() |
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.
maybe add a comment, we should not have any local variable because of using locals()
? Or Deployment can return you a user-configured attributes.
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.
Ah good idea. We can still have local variables, but they should be defined after this list. Creating that list should be the first thing that happens in the function.
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.
Yeah, the order is difficult to guarantee and audit, better to have a way to predefine user-configured attributes.
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.
I think unit tests should be able to catch any issues with the ordering since any local variables that are defined before this list will show up as a user-configured attribute and cause the unit tests to fail. What do you mean by predefine user-configured attributes?
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.
Deployment can provide the user_configured_option_names attributes, you can filter the attributes from locals()
.
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
Signed-off-by: Shreyas Krishnaswamy <[email protected]>
…ct#28313) Signed-off-by: Weichen Xu <[email protected]>
Signed-off-by: Shreyas Krishnaswamy [email protected]
Why are these changes needed?
Background
Serve sets deployment config defaults directly via keyword arguments in the
@serve.deployment
decorator. This makes it it makes it impossible to track whether the config options were set by default or by the user. Distinguishing between these would letserve build
print only the user-configured options, instead of all options (including defaults).Changes
This change adds a layer of indirection in the
@serve.deployment
decorator. It sets all the decorator config options to an enum element,DEFAULT.VALUE
, by default. This element indicates that the config setting was set by default– not by the user. The ServeDeploymentSchema
is also updated to useDEFAULT.VALUE
as default.This change also introduces a new
Set
calleduser_configured_option_names
to theDeploymentConfig
, which tracks the group of options configured by the user. When Serve is schematized or serialized, it can use the names in this set to indicate which options are default and which ones are user-configured.serve build
now prints only user-configured values (as well as deployment names) to the Serve config file.None
is no longer the default value for deployment options. The only options that can be set toNone
(ornull
in the Serve config) are:route_prefix
:None
means the deployment should not be exposed over HTTP.num_replicas
:None
is allowed if anautoscaling_config
is provided.autoscaling_config
:None
is allowed if anum_replicas
is provided.user_config
:None
indicates that there's nouser_config
to pass intoreconfigure
.Follow-up Changes
serve build
should offer a flag that lets users print all options, including the defaults. This is especially useful for users that want to explicitly track all options settings. It may also be useful to always print theroute_prefixes
for Serve deployments if they're notNone
to easily see which deployment is the driver.ray_actor_options
). This change only modifies the thin wrappers over theDeployment
object (i.e. the@serve.deployment
decorator and the Serve schemas) but not theDeployment
object itself.Related issue number
N/A
Checks
git commit -s
) in this PR.scripts/format.sh
to lint the changes in this PR.