Skip to content

Conversation

@jdconrad
Copy link
Contributor

@jdconrad jdconrad commented Dec 15, 2016

Currently, stored scripts use a namespace of (lang, id) to be put, get, deleted, and executed. This is not necessary since the lang is stored with the stored script. A user should only have to specify an id to use a stored script. This change makes that possible while keeping backwards compatibility with the previous namespace of (lang, id). Anywhere the previous namespace is used will log deprecation warnings. The goal is to backport this change to 5.2 and then remove the previous namespace altogether in 6.0 giving users the opportunity to change their scripts to the new namespace.

Why is this change so large?

This change seems huge, but it's not really. The main files changed are ScriptMetaData and ScriptService with StoredScriptSource being added. Secondarily, most of the Request files have changed a bit to support the new/old namespaces. Many lines that have changed are added JavaDocs and some new tests. Also maintaining back compat eats up some lines for XContent and the wire format.

What is the new behavior?

When a user specifies a stored script, that script will be stored under both the new namespace and old namespace.

Take for example script 'A' with lang 'L0' and data 'D0'. If we add script 'A' to the empty set, the scripts map will be ["A" -- D0, "A#L0" -- D0]. If a script 'A' with lang 'L1' and data 'D1' is then added, the scripts map will be ["A" -- D1, "A#L1" -- D1, "A#L0" -- D0].

When a user deletes a stored script, that script will be deleted from both the new namespace (if it exists) and the old namespace.

Take for example a scripts map with {"A" -- D1, "A#L1" -- D1, "A#L0" -- D0}. If a script is removed specified by an id 'A' and lang null then the scripts map will be {"A#L0" -- D0}. To remove the final script, the deprecated namespace must be used, so an id 'A' and lang 'L0' would need to be specified.

When a user gets/executes a stored script, if the new namespace is used then the script will be retrieved/executed using only 'id', and if the old namespace is used then the script will be retrieved/executed using 'id' and 'lang'

@jdconrad jdconrad added :Core/Infra/Scripting Scripting abstractions, Painless, and Mustache v5.2.0 v6.0.0-alpha1 labels Dec 15, 2016
@clintongormley
Copy link
Contributor

@jdconrad Have you added deprecation logging for when the old form (including lang) has been used?

@jdconrad
Copy link
Contributor Author

@clintongormley Sorry about missing the deprecation tag! I think I always miss one. To answer your question I added deprecation logging everywhere user-input can come in from that uses lang.

Copy link
Member

@martijnvg martijnvg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general looks good. A left a number of comments.

I think that we should add some bwc tests too. I think we can add some tests around stored scripts in the qa/rolling-upgrade module?

Also I think in a followup issue, the rest actions / specs should be cleanup so that only the new api remains. This change should only go into master.

// Version 5.2+ will output the contents of the scripts' Map using
// StoredScriptSource to stored the language, code, and options.
if (out.getVersion().onOrAfter(Version.V_5_2_0_UNRELEASED)) {
out.write(scripts.size());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be out.writeVInt(scripts.size());.

Otherwise we overflow after 127 stored scripts :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch! :)

*/
public Builder storeScript(String id, StoredScriptSource source) {
StoredScriptSource previous = scripts.put(id, source);
scripts.put(source.getLang() + "#" + id, source);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder whether we only can add scripts under the new scheme. That way script with old scheme would over time slowly disappear. I think this should work as we already deal with old and new scheme in get and delete?

I think we only need to worry about the update case. I think that is covered if we change the method into this:

StoredScriptSource previous = scripts.remove(source.getLang() + "#" + id);
if (previous != null && previous.getLang().equals(source.getLang()) == false) {
   DEPRECATION_LOGGER.deprecated("stored script [" + id + "] already exists using a different lang " +
                    "[" + previous.getLang() + "], the new namespace for stored scripts will only use (id) instead of (lang, id)");
}
scripts.put(id, source);
return this;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about this as well and it certainly makes sense, but the tricky part is the user has no choice for stored templates. I wanted to make sure that the user could still have a stored template with the same name as a script for this version so they have time to come up with an appropriate plan for the deprecation. With the correct planning the old scheme will simply disappear when loading the scripts from cluster state metadata because we can make it ignore (and print a warning) that an old script cannot be loaded similar to how file scripts work now for scripts that do not compile.

Also, right now this technically isn't a breaking change which I think supports the user a little better (though, hopefully no one is dependent on having scripts of the same name with different languages).

=== Scripting changes



Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accidental. Good catch!

@@ -1,14 +1,20 @@
---
"Indexed script":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not part of this change, but this should be renamed to "Stored script" :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


- do:
warnings:
- 'specifying lang [painless] as part of the url path is deprecated'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I think we should update the yaml specs to just describe the id and update the yaml tests accordingly.
So that the new behavior gets tested too.

We can add get_script_deprecated.json, delete_script_deprecated.json etc. to test that the old behaviour and that warnings get returned in the response.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the tests to the new namespace.

return request.param("lang");
// Note {lang} is actually {id} in the first handler. It appears
// parameters as part of the path must be of the same ordering relative
// to name or they will not work as expected.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true... I also don't see a way to make this nicer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:(, but it will be removed in master anyway

* {@link StoredScriptSource} represents user-defined parameters for a script
* saved in the {@link ClusterState}.
*/
public class StoredScriptSource extends AbstractDiffable<StoredScriptSource> implements Writeable, ToXContent {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 much better than script as bytes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:)

assertThat(rnfe.getMessage(), equalTo("stored script [test] does not exist and cannot be deleted"));
}

public void testSourceParsing() throws Exception {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@colings86 is adding base test classes for writeable and xcontent serialization / de-serialization via #22281

That should get merged quickly. When that is in can you make this test class extend from AbstractSerializingTestCase. That will add random serialization tests in addition that the serialization that exist already.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and maybe do the same for ScriptMetaDataTests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


int split = id.indexOf('#');

if (split == -1) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can move the split check to the place where we assign the id variable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

split is used in both the if and else blocks

@jdconrad
Copy link
Contributor Author

@martijnvg Thank you so much for reviewing this! I agree that the old API should be removed in master -- the plan was to get this in, backport it to version 5.2 (although maybe 5.3 is more realistic given the upcoming holidays), and then remove the old API in master where we can.
I'll get started working on your other comments pretty soon.

@jdconrad jdconrad added v5.3.0 and removed v5.2.0 labels Jan 4, 2017
@jdconrad
Copy link
Contributor Author

Added tests for rolling upgrade. These will need to be updated when the code is backported to 5.3 and when 6.0 deletes the code for the old API.

@jdconrad
Copy link
Contributor Author

@martijnvg I think this is ready for another round of review. I believe I addressed all the comments, and added several tests that you requested. Thanks!

@jdconrad
Copy link
Contributor Author

@martijnvg I added the patch you sent me to address the bwc bugs you found with one minor change -- I decided to clean up the Script readFrom and writeTo a little bit to separate out each of the read and write versions into their own if/else block as I think that will help clear up potential future confusion. And thanks again so much for catching some of the bwc bugs! Ready for another round of review when you have the chance.

Copy link
Member

@martijnvg martijnvg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thx Jack! LGTM

Maybe also open an issue to track to removal of the lang parameter from the script/template apis and internal classes for 6.0?

@jdconrad
Copy link
Contributor Author

@martijnvg Thanks for all the reviews here! Added this as a follow up issue (#22887) I will commit this code once I get master integrated properly and all the tests passing.

@jdconrad jdconrad merged commit 3d2626c into elastic:master Jan 31, 2017
jdconrad added a commit that referenced this pull request Feb 1, 2017
Currently, stored scripts use a namespace of (lang, id) to be put, get, deleted, and executed. This is not necessary since the lang is stored with the stored script. A user should only have to specify an id to use a stored script. This change makes that possible while keeping backwards compatibility with the previous namespace of (lang, id). Anywhere the previous namespace is used will log deprecation warnings.

The new behavior is the following:

When a user specifies a stored script, that script will be stored under both the new namespace and old namespace.

Take for example script 'A' with lang 'L0' and data 'D0'. If we add script 'A' to the empty set, the scripts map will be ["A" -- D0, "A#L0" -- D0]. If a script 'A' with lang 'L1' and data 'D1' is then added, the scripts map will be ["A" -- D1, "A#L1" -- D1, "A#L0" -- D0].

When a user deletes a stored script, that script will be deleted from both the new namespace (if it exists) and the old namespace.

Take for example a scripts map with {"A" -- D1, "A#L1" -- D1, "A#L0" -- D0}. If a script is removed specified by an id 'A' and lang null then the scripts map will be {"A#L0" -- D0}. To remove the final script, the deprecated namespace must be used, so an id 'A' and lang 'L0' would need to be specified.

When a user gets/executes a stored script, if the new namespace is used then the script will be retrieved/executed using only 'id', and if the old namespace is used then the script will be retrieved/executed using 'id' and 'lang'
@clintongormley
Copy link
Contributor

@jdconrad in 6.0 i assume the namespace support will no longer be deprecated but will be removed? If so, please add a note to the migration docs for 6.0

@jdconrad
Copy link
Contributor Author

jdconrad commented Feb 6, 2017

@clintongormley Yes, it will be removed, but that requires another change to actually remove it from 6.0 that I'll try to follow up with this week. Right now, it's still technically deprecated there, but not removed. Once, I make the change I'll make sure to add that as a (breaking?) note to the migration docs.

@jdconrad jdconrad deleted the stored4 branch February 21, 2017 18:40
@Mpdreamz
Copy link
Member

@jdconrad should the spec for script api's in master be changed from:

      "path": "/_scripts/{lang}",
      "paths": [ "/_scripts/{lang}", "/_scripts/{lang}/{id}" ],

to

      "path": "/_scripts/{id}",
      "paths": [ "/_scripts/{id}" ],

Or has the removal not made it in yet to master ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

:Core/Infra/Scripting Scripting abstractions, Painless, and Mustache >deprecation v5.3.0 v6.0.0-alpha1

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants