-
Notifications
You must be signed in to change notification settings - Fork 24.7k
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
add support for write index resolution when creating/updating documents #31520
Changes from 5 commits
9e86921
df8fabd
7c75916
113ebc5
fb7c168
c1d2d4e
b585835
42893bf
54d0a3d
b5e5e81
d4a5ebd
2f8a004
9238cb5
8ae0967
e8cf8c8
2f2af95
3df26bd
5d47213
00eb394
7c753db
f1032e4
347149e
e58a3fb
222ae13
ae372f3
b54efe4
cda911f
eb097c2
cc969d1
284d3ee
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 |
---|---|---|
|
@@ -46,13 +46,15 @@ | |
*/ | ||
public class ReindexSourceTargetValidationTests extends ESTestCase { | ||
private static final ClusterState STATE = ClusterState.builder(new ClusterName("test")).metaData(MetaData.builder() | ||
.put(index("target", "target_alias", "target_multi"), true) | ||
.put(index("target2", "target_multi"), true) | ||
.put(index("foo"), true) | ||
.put(index("bar"), true) | ||
.put(index("baz"), true) | ||
.put(index("source", "source_multi"), true) | ||
.put(index("source2", "source_multi"), true)).build(); | ||
.put(index("target", false, "target_alias", "target_multi"), true) | ||
.put(index("target2", false, "target_multi"), true) | ||
.put(index("target3", true, "target_multi_with_write"), true) | ||
.put(index("target4", false, "target_multi_with_write"), true) | ||
.put(index("foo", false), true) | ||
.put(index("bar", false), true) | ||
.put(index("baz", false), true) | ||
.put(index("source", false, "source_multi"), true) | ||
.put(index("source2", false, "source_multi"), true)).build(); | ||
private static final IndexNameExpressionResolver INDEX_NAME_EXPRESSION_RESOLVER = new IndexNameExpressionResolver(Settings.EMPTY); | ||
private static final AutoCreateIndex AUTO_CREATE_INDEX = new AutoCreateIndex(Settings.EMPTY, | ||
new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), INDEX_NAME_EXPRESSION_RESOLVER); | ||
|
@@ -84,6 +86,7 @@ public void testTargetIsAlias() { | |
// The index names can come in either order | ||
assertThat(e.getMessage(), containsString("target")); | ||
assertThat(e.getMessage(), containsString("target2")); | ||
// NORELEASE: succeeds("target_multi_with_write", "foo"); should this be allowed? | ||
} | ||
|
||
public void testRemoteInfoSkipsValidation() { | ||
|
@@ -109,7 +112,7 @@ private void succeeds(RemoteInfo remoteInfo, String target, String... sources) { | |
INDEX_NAME_EXPRESSION_RESOLVER, AUTO_CREATE_INDEX, STATE); | ||
} | ||
|
||
private static IndexMetaData index(String name, String... aliases) { | ||
private static IndexMetaData index(String name, boolean isWriteIndex, String... aliases) { | ||
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 param isn't used? |
||
IndexMetaData.Builder builder = IndexMetaData.builder(name).settings(Settings.builder() | ||
.put("index.version.created", Version.CURRENT.id) | ||
.put("index.number_of_shards", 1) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -295,7 +295,7 @@ protected void doRun() throws Exception { | |
TransportUpdateAction.resolveAndValidateRouting(metaData, concreteIndex.getName(), (UpdateRequest) docWriteRequest); | ||
break; | ||
case DELETE: | ||
docWriteRequest.routing(metaData.resolveIndexRouting(docWriteRequest.routing(), docWriteRequest.index())); | ||
docWriteRequest.routing(metaData.resolveIndexRouting(docWriteRequest.routing(), docWriteRequest.index(), true)); | ||
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. double checking - do we have a test for multi routing values in alias and make sure we use the right value (I hate this API but we have to test it works)? 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. With some more thinking, I don't think we should make this feature (routing value in an alias) work with the write index flag. The problem is that if we allow to write to multi index aliases with this feature turned on, there is no way for get to work through those aliases without us changing the get behavior of resolving multi-index aliased base on the index write flag which is unexpected. Bottom line, can we see what it takes to make sure resolveIndexRouting explodes as soon as you touch more than one index via an alias? |
||
// check if routing is required, if so, throw error if routing wasn't specified | ||
if (docWriteRequest.routing() == null && metaData.routingRequired(concreteIndex.getName(), docWriteRequest.type())) { | ||
throw new RoutingMissingException(concreteIndex.getName(), docWriteRequest.type(), docWriteRequest.id()); | ||
|
@@ -474,7 +474,7 @@ Index getConcreteIndex(String indexOrAlias) { | |
Index resolveIfAbsent(DocWriteRequest<?> request) { | ||
Index concreteIndex = indices.get(request.index()); | ||
if (concreteIndex == null) { | ||
concreteIndex = indexNameExpressionResolver.concreteSingleIndex(state, request); | ||
concreteIndex = indexNameExpressionResolver.concreteWriteIndex(state, request); | ||
indices.put(request.index(), concreteIndex); | ||
} | ||
return concreteIndex; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -103,7 +103,7 @@ public String[] concreteIndexNames(ClusterState state, IndicesOptions options, S | |
return concreteIndexNames(context, indexExpressions); | ||
} | ||
|
||
/** | ||
/** | ||
* Translates the provided index expression into actual concrete indices, properly deduplicated. | ||
* | ||
* @param state the cluster state containing all the data to resolve to expressions to concrete indices | ||
|
@@ -117,7 +117,26 @@ public String[] concreteIndexNames(ClusterState state, IndicesOptions options, S | |
* indices options in the context don't allow such a case. | ||
*/ | ||
public Index[] concreteIndices(ClusterState state, IndicesOptions options, String... indexExpressions) { | ||
Context context = new Context(state, options); | ||
Context context = new Context(state, options, false, false); | ||
return concreteIndices(context, indexExpressions); | ||
} | ||
|
||
/** | ||
* Translates the provided index expression into actual concrete indices, properly deduplicated. | ||
* | ||
* @param state the cluster state containing all the data to resolve to expressions to concrete indices | ||
* @param options defines how the aliases or indices need to be resolved to concrete indices | ||
* @param resolveToWriteIndex defines whether to require that aliases resolve to their respective write indices | ||
* @param indexExpressions expressions that can be resolved to alias or index names. | ||
* @return the resolved concrete indices based on the cluster state, indices options and index expressions | ||
* @throws IndexNotFoundException if one of the index expressions is pointing to a missing index or alias and the | ||
* provided indices options in the context don't allow such a case, or if the final result of the indices resolution | ||
* contains no indices and the indices options in the context don't allow such a case. | ||
* @throws IllegalArgumentException if one of the aliases resolve to multiple indices and the provided | ||
* indices options in the context don't allow such a case. | ||
*/ | ||
public Index[] concreteIndices(ClusterState state, IndicesOptions options, boolean resolveToWriteIndex, String... indexExpressions) { | ||
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 does this method need to be public ? can we remove it and inline the implementation with it's single caller? it's just two lines. |
||
Context context = new Context(state, options, false, resolveToWriteIndex); | ||
return concreteIndices(context, indexExpressions); | ||
} | ||
|
||
|
@@ -194,29 +213,43 @@ Index[] concreteIndices(Context context, String... indexExpressions) { | |
} | ||
|
||
Collection<IndexMetaData> resolvedIndices = aliasOrIndex.getIndices(); | ||
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 make if clause have this structure and inline this variable in the second else (see marker):
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. ping about inlining this variable. |
||
if (resolvedIndices.size() > 1 && !options.allowAliasesToMultipleIndices()) { | ||
|
||
if (aliasOrIndex.isAlias() && context.isResolveToWriteIndex()) { | ||
AliasOrIndex.Alias alias = (AliasOrIndex.Alias) aliasOrIndex; | ||
IndexMetaData writeIndex = alias.getWriteIndex(); | ||
if (writeIndex == null) { | ||
if (alias.getIndices().size() > 1) { | ||
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. I don't think you can make this statement - it may be that all of them have their write index flag set to false. I think we can have a generic statement like "no write index is defined for alias X. The write index may be explicitly disabled using 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. I will make more generic 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.
saying it points to multiple indices with none set as a write-index [is_write_index=true] does not conflict with that statement? 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. isn't this covered by "the alias points to multiple indices without one being designated as a write index"? |
||
throw new IllegalArgumentException("Alias [" + alias.getAliasName() + | ||
"] points to multiple indices with none set as a write-index [is_write_index=true]"); | ||
} else { | ||
throw new IllegalArgumentException("Alias [" + alias.getAliasName() + "] points to an index [" | ||
+ alias.getIndices().get(0).getIndex().getName() + "] with [is_write_index=false]"); | ||
} | ||
} | ||
concreteIndices.add(writeIndex.getIndex()); | ||
} else if (resolvedIndices.size() > 1 && options.allowAliasesToMultipleIndices() == false) { | ||
String[] indexNames = new String[resolvedIndices.size()]; | ||
int i = 0; | ||
for (IndexMetaData indexMetaData : resolvedIndices) { | ||
indexNames[i++] = indexMetaData.getIndex().getName(); | ||
} | ||
throw new IllegalArgumentException("Alias [" + expression + "] has more than one indices associated with it [" + | ||
Arrays.toString(indexNames) + "], can't execute a single index op"); | ||
} | ||
|
||
for (IndexMetaData index : resolvedIndices) { | ||
if (index.getState() == IndexMetaData.State.CLOSE) { | ||
if (failClosed) { | ||
throw new IndexClosedException(index.getIndex()); | ||
} else { | ||
if (options.forbidClosedIndices() == false) { | ||
concreteIndices.add(index.getIndex()); | ||
Arrays.toString(indexNames) + "], can't execute a single index op"); | ||
} else { | ||
for (IndexMetaData index : resolvedIndices) { | ||
if (index.getState() == IndexMetaData.State.CLOSE) { | ||
if (failClosed) { | ||
throw new IndexClosedException(index.getIndex()); | ||
} else { | ||
if (options.forbidClosedIndices() == false) { | ||
concreteIndices.add(index.getIndex()); | ||
} | ||
} | ||
} else if (index.getState() == IndexMetaData.State.OPEN) { | ||
concreteIndices.add(index.getIndex()); | ||
} else { | ||
throw new IllegalStateException("index state [" + index.getState() + "] not supported"); | ||
} | ||
} else if (index.getState() == IndexMetaData.State.OPEN) { | ||
concreteIndices.add(index.getIndex()); | ||
} else { | ||
throw new IllegalStateException("index state [" + index.getState() + "] not supported"); | ||
} | ||
} | ||
} | ||
|
@@ -255,6 +288,24 @@ public Index concreteSingleIndex(ClusterState state, IndicesRequest request) { | |
return indices[0]; | ||
} | ||
|
||
/** | ||
* Utility method that allows to resolve an index expression to its corresponding single write index. | ||
* | ||
* @param state the cluster state containing all the data to resolve to expression to a concrete index | ||
* @param request The request that defines how the an alias or an index need to be resolved to a concrete index | ||
* and the expression that can be resolved to an alias or an index name. | ||
* @throws IllegalArgumentException if the index resolution does not lead to an index, or leads to more than one index | ||
* @return the write index obtained as a result of the index resolution | ||
*/ | ||
public Index concreteWriteIndex(ClusterState state, IndicesRequest request) { | ||
String indexExpression = request.indices() != null && request.indices().length > 0 ? request.indices()[0] : null; | ||
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. I don't think null is good hear (it translates to an all). 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 would this be different than concreteSingleIndex? |
||
Index[] indices = concreteIndices(state, request.indicesOptions(), true, indexExpression); | ||
if (indices.length != 1) { | ||
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. 💯 |
||
throw new IllegalArgumentException("unable to return a single index as the index and options provided got resolved to multiple indices"); | ||
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. I think we need to adapt this message for more things that can go wrong (single index with no write flag etc.) |
||
} | ||
return indices[0]; | ||
} | ||
|
||
/** | ||
* @return whether the specified alias or index exists. If the alias or index contains datemath then that is resolved too. | ||
*/ | ||
|
@@ -292,7 +343,7 @@ public String[] indexAliases(ClusterState state, String index, Predicate<AliasMe | |
String... expressions) { | ||
// expand the aliases wildcard | ||
List<String> resolvedExpressions = expressions != null ? Arrays.asList(expressions) : Collections.emptyList(); | ||
Context context = new Context(state, IndicesOptions.lenientExpandOpen(), true); | ||
Context context = new Context(state, IndicesOptions.lenientExpandOpen(), true, false); | ||
for (ExpressionResolver expressionResolver : expressionResolvers) { | ||
resolvedExpressions = expressionResolver.resolve(context, resolvedExpressions); | ||
} | ||
|
@@ -512,24 +563,26 @@ static final class Context { | |
private final IndicesOptions options; | ||
private final long startTime; | ||
private final boolean preserveAliases; | ||
private final boolean resolveToWriteIndex; | ||
|
||
Context(ClusterState state, IndicesOptions options) { | ||
this(state, options, System.currentTimeMillis()); | ||
} | ||
|
||
Context(ClusterState state, IndicesOptions options, boolean preserveAliases) { | ||
this(state, options, System.currentTimeMillis(), preserveAliases); | ||
Context(ClusterState state, IndicesOptions options, boolean preserveAliases, boolean resolveToWriteIndex) { | ||
this(state, options, System.currentTimeMillis(), preserveAliases, resolveToWriteIndex); | ||
} | ||
|
||
Context(ClusterState state, IndicesOptions options, long startTime) { | ||
this(state, options, startTime, false); | ||
this(state, options, startTime, false, false); | ||
} | ||
|
||
Context(ClusterState state, IndicesOptions options, long startTime, boolean preserveAliases) { | ||
Context(ClusterState state, IndicesOptions options, long startTime, boolean preserveAliases, boolean resolveToWriteIndex) { | ||
this.state = state; | ||
this.options = options; | ||
this.startTime = startTime; | ||
this.preserveAliases = preserveAliases; | ||
this.resolveToWriteIndex = resolveToWriteIndex; | ||
} | ||
|
||
public ClusterState getState() { | ||
|
@@ -552,6 +605,14 @@ public long getStartTime() { | |
boolean isPreserveAliases() { | ||
return preserveAliases; | ||
} | ||
|
||
/** | ||
* This is used to require that aliases resolve to their write-index. It is currently not used in conjunction | ||
* with <code>preserveAliases</code>. | ||
*/ | ||
boolean isResolveToWriteIndex() { | ||
return resolveToWriteIndex; | ||
} | ||
} | ||
|
||
private interface ExpressionResolver { | ||
|
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 class needs more work, but I am not sure whether we want reindex target-aliases to resolve to
the write index. It feels right, but it also sounds scary. what do you think?
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.
oh nevermind, it must check details since even a single index with
is_write_index=false
should not be chosen by reindex api, and that would be awkwardThere 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'm not sure I follow. Reindex should be orthogonal to the write alias and should just work ™️ ? what am I missing?