-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
[MLflowSkinny] Base changes for MLflow Skinny Client #3687
[MLflowSkinny] Base changes for MLflow Skinny Client #3687
Conversation
ef19ead
to
5906e23
Compare
@@ -8,7 +8,7 @@ | |||
packages=find_packages(), | |||
# Require MLflow as a dependency of the plugin, so that plugin users can simply install | |||
# the plugin & then immediately use it with MLflow | |||
install_requires=["mlflow"], | |||
install_requires=[], # Remove mlflow dependency, it can cause tests to run against pypi |
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.
@harupy @smurching Didn't this come up in the context of another ticket? Can you comment on how to best resolve it?
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 did yeah - we were confused why e.g. gorilla
was still being installed in our test environment despite the fact that we'd removed it as a dependency from setup.py
, e.g. in https://github.com/mlflow/mlflow/pull/3666/checks?check_run_id=1382567094#step:3:308. It turns out in our dependency installation scripts for Github actions, we install this test plugin and then install MLflow from source - because we install the test plugin first, we end up installing mlflow
from pypi as a transitive dependency of the plugin, and then overwriting that installation with MLflow from source.
The change here avoids that problem by removing mlflow
as a dependency from the test plugin. This seems ok as we ought to only use the test plugin in our CI - in practice though, we reference the test plugin in our docs on writing plugins.
To get around this we could either:
a) Define another example plugin to reference in our docs & add tests verifying that it works (seems like more work)
b) See if we can do something like install_requires=[os.path.join(".", os.pardir, os.pardir, os.pardir)]
to depend on MLflow installed from source (reduces the value of the test plugin as an example)
c) Just update our install-common-deps.sh CI script to install MLflow from source before we install other Python dependencies, including the test plugin
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.
IMO option a) is maybe the best long-term approach, but I'd be fine with option c) too in practice as a quick fix for this issue; the risk is that one of our test requirements has a strict version dependency on mlflow
and ends up overwriting our source installation of mlflow
, but this seems unlikely / we could potentially test for this
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.
As discussed offline with @smurching , I think the concern here is that depending on mlflow
(even a source-installed copy) will bring in dependencies that aren't included by the skinny client, degrading the utility of our skinny client tests.
As a result, I think it's best for us to adopt option (a) here. @eedeleon Is this something you can do as part of this PR? If not, I can pitch in.
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 we could implement a like b.
requirements = [] if MLFLOW_TEST_ENV in os.environ else ["mlflow"]
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.
We can add a test that verifies MLFLOW_TEST_ENV(we can use any env var) is set as well
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.
That os.environ
code will show up in our example reference too, though, which we probably don't want.
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.
We can add a test that verifies MLFLOW_TEST_ENV(we can use any env var) is set as well
Agreed!
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.
@eedeleon Sorry for the delay here; I left a few comments. This is close!
|
||
pytest --verbose tests/test_skinny.py | ||
python -m pip install sqlalchemy alembic sqlparse | ||
pytest --verbose tests/tracking/test_client.py |
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.
Since we're installing sqlalchemy / alembic / sqlparse for integration testing of tracking APIs, can we run test_tracking.py
in addition to test_client.py
? test_client.py
relies on mocked backend stores & doesn't seem to explicitly require sqlalchemy functionality.
In a future PR, we should more explicitly restructure our test coverage to separate unit & integration tests for clarity, as per our offline discussion. E.g.,:
- Tracking
- Client
- Unit (Tests that don’t depend on a backend)
- Integration (Tests for client interaction against a backend)
- Server
- Unit (Backend / sever tests that don’t depend on a client)
- Integration (Backend / server tests that depend on a client - e.g., `test_rest_tracking.py`)
Leaving this here as a reminder for us to revisit test structure prior to a skinny client release. More than happy to formalize this as a more complete refactoring plan!
@@ -5,9 +5,6 @@ | |||
import requests |
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.
The skinny client will eventually need to support databricks_artifact_repo
for compatibility with Databricks. Leaving this here as a note for myself to remove the dependency on azure.storage.blob.BlobClient
for this store. We've done this already for a different compatibility purpose on Databricks, so it should be relatively quick to apply this change.
@@ -8,7 +8,7 @@ | |||
packages=find_packages(), | |||
# Require MLflow as a dependency of the plugin, so that plugin users can simply install | |||
# the plugin & then immediately use it with MLflow | |||
install_requires=["mlflow"], | |||
install_requires=[], # Remove mlflow dependency, it can cause tests to run against pypi |
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.
As discussed offline with @smurching , I think the concern here is that depending on mlflow
(even a source-installed copy) will bring in dependencies that aren't included by the skinny client, degrading the utility of our skinny client tests.
As a result, I think it's best for us to adopt option (a) here. @eedeleon Is this something you can do as part of this PR? If not, I can pitch in.
e305ecc
to
c03aa39
Compare
mlflow/azureml/__init__.py
Outdated
try: | ||
from mlflow.utils.proto_json_utils import _get_jsonable_obj | ||
except ImportError: # Back Compat for https://github.com/mlflow/mlflow/pull/3687 | ||
from mlflow.pyfunc.scoring_server import _get_jsonable_obj |
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 the concern here that we might deploy an MLflow model to AzureML and install an older version of MLflow when creating the serving container Docker image? The deployment procedure seems to ensure that we're pulling in the latest MLflow version (
mlflow/mlflow/azureml/__init__.py
Lines 554 to 561 in aaed804
if mlflow_path is not None: | |
mlflow_install_cmd = "RUN pip install -e {mlflow_path}".format( | |
mlflow_path=_get_container_path(mlflow_path) | |
) | |
elif not mlflow_version.endswith("dev"): | |
mlflow_install_cmd = "RUN pip install mlflow=={mlflow_version}".format( | |
mlflow_version=mlflow_version | |
) |
Let me know if I'm missing something here.
setup.py
Outdated
"simplejson", | ||
] | ||
|
||
_is_mlflow_skinny = bool(os.environ.get(_MLFLOW_SKINNY_ENV_VAR)) |
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.
Nit: Can we move this down to line 78-ish (right above setup()
). The var naming + comments look awesome - thanks so much for the investment there!
tests/deployments/test_cli.py
Outdated
@@ -91,13 +91,3 @@ def test_target_help(): | |||
runner = CliRunner() | |||
res = runner.invoke(cli.target_help, ["--target", f_target]) | |||
assert "Target help is called" in res.stdout | |||
|
|||
|
|||
def test_run_local(): |
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.
Sorry, I think I got this wrong. This run_local
doesn't seem like it actually starts a flask server. Can we revert this move?
"sqlalchemy<=1.3.13", | ||
"waitress; platform_system == 'Windows'", | ||
] | ||
|
||
setup( | ||
name="mlflow", | ||
version=version, | ||
packages=find_packages(exclude=["tests", "tests.*"]), | ||
package_data={"mlflow": js_files + models_container_server_files + alembic_files}, |
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 don't think we should embed the UI JS, model container server files, or alembic files with the skinny client. Can we condition adding these files on _is_mlflow_skinny
and omit them if skinny is 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.
Hmm...looks like these files are included independently of package_data
. I think find_packages
picks them up. Let's follow up here in a future PR.
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.
@eedeleon This is really close. I left a few more comments.
The most significant thing I noticed while trying this out is that attempting to run the MLflow CLI fails with an alembic import error:
import mlflow.cli
ModuleNotFoundError: No module named 'alembic'
Ideally, we should provide test coverage for the MLflow CLI. I noticed that test_cli
aggregates test cases for mlflow server
, mlflow ui
, mlflow gc
, and mlflow run
. Perhaps we could split this file up: 1. add the mlflow server
/ mlflow ui
/mlflow gc
tests to a new test_cli
suite in the tests/server
subdirectory & 2. add the mlflow run
tests to test_projects_cli
.
Otherwise, I've confirmed that the following workflows are supported by the skinny client on Databricks:
-
Tracking operations (logging / loading / searching params, metrics, tags + logging / loading artifacts on AWS - Azure requires removal of the Azure Storage Blob dependency as a follow-up PR)
-
Model registration, search, artifact loading, transitions
-
Execution of GitHub projects within a Databricks notebook & against a Databricks workspace remotely
-
After installing scikit-learn, sklearn autologging + model log / load operations succeed
551442b
to
048b67c
Compare
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.
@eedeleon LGTM once a few small comments have been addressed! Thanks so much for this contribution!
Here's a TODO list of follow-up tasks I've put together; ones necessary prior to the initial skinny client release are tagged [Required for 1.14]
:
-
[Required for 1.14] Figure out how to distribute the client. E.g., add to MLflow release CLI scaffolding maintained by Databricks
-
[Required for 1.14] Document the skinny client on
mlflow.org
/ in the GitHub repository (what does the skinny client do and what does it not do) -
[Required for 1.14] Drop azure-storage-blob requirement for artifact logging on Azure Databricks
-
Drop pandas and numpy requirements
-
Test restructure: https://github.com/mlflow/mlflow/pull/3687/files#r528014570
mlflow/cli.py
Outdated
try: | ||
import mlflow.sagemaker.cli | ||
|
||
cli.add_command(mlflow.sagemaker.cli.commands) | ||
except (ImportError, ModuleNotFoundError) as e: | ||
_logger.warning("Failed to import mlflow.sagemaker.cli with %s.", e) | ||
|
||
try: | ||
import mlflow.azureml.cli | ||
|
||
cli.add_command(mlflow.azureml.cli.commands) | ||
except (ImportError, ModuleNotFoundError) as e: | ||
_logger.warning("Failed to import mlflow.azureml.cli with %s.", e) | ||
|
||
try: | ||
import mlflow.db | ||
|
||
cli.add_command(mlflow.db.commands) | ||
except (ImportError, ModuleNotFoundError) as e: | ||
_logger.warning("Failed to import mlflow.db with %s.", e) |
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.
@eedeleon Looks like azureml
and sagemaker
CLI command imports both succeed. mlflow.db
succeeds if we move
Line 3 in da9a7d1
import mlflow.store.db.utils |
Line 16 in da9a7d1
def upgrade(url): |
I think it might be easier to make the fix for db.py
right now, rather than adding the warning log. Otherwise, the user sees a warning message every time they use the mlflow CLI, which looks like an error that the user needs to address rather than an expected limitation of the skinny client:
mlflow --help
2020/12/17 11:56:40 WARNING mlflow.cli: Failed to import mlflow.db with No module named 'alembic'.
Usage: mlflow [OPTIONS] COMMAND [ARGS]...
Options:
--version Show the version and exit.
--help Show this message and exit.
Commands:
artifacts Upload, list, and download artifacts from an MLflow artifact...
azureml Serve models on Azure ML.
deployments Deploy MLflow models to custom targets.
experiments Manage experiments.
gc Permanently delete runs in the `deleted` lifecycle stage.
models Deploy MLflow models locally.
run Run an MLflow project from the given URI.
runs Manage runs.
sagemaker Serve models on SageMaker.
server Run the MLflow tracking server.
ui Launch the MLflow tracking UI for local viewing of run...
d72feb4
to
5502a82
Compare
Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
3b3e293
to
c7ec118
Compare
Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
dev/run-python-skinny-tests.sh
Outdated
trap 'err=1' ERR | ||
export MLFLOW_SKINNY='true' | ||
|
||
python -m pip uninstall sqlalchemy alembic sqlparse -y |
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.
@eedeleon Is this necessary? sqlalchemy
, alembic
, and sqlparse
shouldn't be present when we install the skinny client. Explicitly uninstalling them seems to allow for them to be present initially, indicating that our tests are permitting an erroneous condition.
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.
sqlalchemy was recently added to small-requirements.txt.
I can create a skinny-requirements.txt and use that to avoid further issues but it seemed like overkill.
Should I go with the skinny-requirements.txt or only uninstall sqlalchemy?
tests/test_cli_run.py
Outdated
@@ -0,0 +1,69 @@ | |||
from click.testing import CliRunner |
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.
@eedeleon Can we move these into tests/projects/cli.py
?
At a glance, it doesn't really seem like any of these test cases should be causing the permissions-related issue described in #3888.
…w_run tests, uninstall sqlalchemy before skinny client installation in install common deps Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
dev/install-common-deps.sh
Outdated
# Skinny Client purposefully removes the sqlalchemy dependency | ||
if [[ "$MLFLOW_SKINNY" == "true" ]]; then | ||
pip uninstall sqlalchemy -y | ||
fi |
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.
@eedeleon How do we know that installing MLflow Skinny didn't pull in sqlalchemy
independently of the Python small dependencies? We could change the ordering here so that the MLflow client is installed after the small dependencies & removal of sqlalchemy
, but I'd rather create a skinny-requirements
file instead of relying on subtle ordering properties / environment-variable-based branching logic in install-common-deps
.
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 made the change. There is some overlap between small-requirements and skinny requirements, but I agree it is worth it since this approach is definitely more explicit and less likely to leak bugs.
Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
* Add skinny client Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Add split off cli server tests Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Add chmod to skinny test files Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Resolve setup.py differences Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Move the fixture up a level Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Resolve imports Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Move in sqlalchemy import Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Handle lint issue Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Resolve lint issues Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Apply formatting Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Resolve test failures Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Undo conftest changes Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Move test_csv_generation to test_runs.py, remove duplicate test_mlflow_run tests, uninstall sqlalchemy before skinny client installation in install common deps Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Resolve sqlalchemy uninstall ordering Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Undo odd copy bug to vscode in csv_generation tests Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Add skinny-requirements for skinny tests Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Move docker import into docker util function Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com>
* Add skinny client Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Add split off cli server tests Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Add chmod to skinny test files Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Resolve setup.py differences Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Move the fixture up a level Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Resolve imports Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Move in sqlalchemy import Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Handle lint issue Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Resolve lint issues Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Apply formatting Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Resolve test failures Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Undo conftest changes Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Move test_csv_generation to test_runs.py, remove duplicate test_mlflow_run tests, uninstall sqlalchemy before skinny client installation in install common deps Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Resolve sqlalchemy uninstall ordering Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Undo odd copy bug to vscode in csv_generation tests Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Add skinny-requirements for skinny tests Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Move docker import into docker util function Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> Signed-off-by: harupy <17039389+harupy@users.noreply.github.com>
* Add skinny client Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Add split off cli server tests Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Add chmod to skinny test files Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Resolve setup.py differences Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Move the fixture up a level Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Resolve imports Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Move in sqlalchemy import Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Handle lint issue Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Resolve lint issues Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Apply formatting Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Resolve test failures Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Undo conftest changes Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Move test_csv_generation to test_runs.py, remove duplicate test_mlflow_run tests, uninstall sqlalchemy before skinny client installation in install common deps Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Resolve sqlalchemy uninstall ordering Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Undo odd copy bug to vscode in csv_generation tests Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Add skinny-requirements for skinny tests Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> * Move docker import into docker util function Signed-off-by: Eduardo de Leon <eddeleon@microsoft.com> Signed-off-by: harupy <17039389+harupy@users.noreply.github.com>
What changes are proposed in this pull request?
Adding base changes for MLflow to run with all but numpy, pandas, and sqlparse dependencies removed.
How is this patch tested?
Runs a subset of existing tests to unblock parallel development streams on mlflow skinny client
Release Notes
Is this a user-facing change?
(Details in 1-2 sentences. You can just refer to another PR with a description if this PR is part of a larger change.)
What component(s), interfaces, languages, and integrations does this PR affect?
Components
area/artifacts
: Artifact stores and artifact loggingarea/build
: Build and test infrastructure for MLflowarea/docs
: MLflow documentation pagesarea/examples
: Example codearea/model-registry
: Model Registry service, APIs, and the fluent client calls for Model Registryarea/models
: MLmodel format, model serialization/deserialization, flavorsarea/projects
: MLproject format, project running backendsarea/scoring
: Local serving, model deployment tools, spark UDFsarea/server-infra
: MLflow server, JavaScript dev serverarea/tracking
: Tracking Service, tracking client APIs, autologgingInterface
area/uiux
: Front-end, user experience, JavaScript, plottingarea/docker
: Docker use across MLflow's components, such as MLflow Projects and MLflow Modelsarea/sqlalchemy
: Use of SQLAlchemy in the Tracking Service or Model Registryarea/windows
: Windows supportLanguage
language/r
: R APIs and clientslanguage/java
: Java APIs and clientslanguage/new
: Proposals for new client languagesIntegrations
integrations/azure
: Azure and Azure ML integrationsintegrations/sagemaker
: SageMaker integrationsintegrations/databricks
: Databricks integrationsHow should the PR be classified in the release notes? Choose one:
rn/breaking-change
- The PR will be mentioned in the "Breaking Changes" sectionrn/none
- No description will be included. The PR will be mentioned only by the PR number in the "Small Bugfixes and Documentation Updates" sectionrn/feature
- A new user-facing feature worth mentioning in the release notesrn/bug-fix
- A user-facing bug fix worth mentioning in the release notesrn/documentation
- A user-facing documentation change worth mentioning in the release notes