Skip to content
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

Merged
merged 30 commits into from
Jul 19, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
9e86921
add support for write index resolution when creating/updating documents
talevy Jun 20, 2018
df8fabd
refactor to use new resolveWriteIndex flag instead of IndicesOptions
talevy Jun 25, 2018
7c75916
Merge branch 'master' into indices-options-write-index
talevy Jun 26, 2018
113ebc5
comment out test question
talevy Jun 26, 2018
fb7c168
whitespace cleanup
talevy Jun 26, 2018
c1d2d4e
Merge branch 'master' into indices-options-write-index
talevy Jun 27, 2018
b585835
cleanup and return existing alias routing resolution
talevy Jun 27, 2018
42893bf
Merge branch 'master' into indices-options-write-index
talevy Jun 27, 2018
54d0a3d
fix typo
talevy Jun 27, 2018
b5e5e81
add successful getWriteIndex test
talevy Jun 27, 2018
d4a5ebd
simplify metadata test
talevy Jun 27, 2018
2f8a004
ease up index routing resolution.
talevy Jun 27, 2018
9238cb5
fix IT test
talevy Jun 28, 2018
8ae0967
Merge branch 'master' into indices-options-write-index
talevy Jun 28, 2018
e8cf8c8
add security test for is_write_index resolution
talevy Jun 29, 2018
2f2af95
Merge branch 'master' into indices-options-write-index
talevy Jun 29, 2018
3df26bd
Merge branch 'master' into indices-options-write-index
talevy Jul 9, 2018
5d47213
Merge branch 'master' into indices-options-write-index
talevy Jul 10, 2018
00eb394
respond to comments
talevy Jul 10, 2018
7c753db
add tests for different index expressions and indices options
talevy Jul 10, 2018
f1032e4
fix checkstyle
talevy Jul 10, 2018
347149e
fix metadata index routing tests
talevy Jul 10, 2018
e58a3fb
ignore routing on aliases pointing to multiple indices in write opera…
talevy Jul 10, 2018
222ae13
Merge branch 'master' into indices-options-write-index
talevy Jul 15, 2018
ae372f3
update routing to evaluate to write index's alias metadata for write …
talevy Jul 16, 2018
b54efe4
Merge branch 'master' into indices-options-write-index
talevy Jul 17, 2018
cda911f
add more integration tests and small cleanup
talevy Jul 17, 2018
eb097c2
fix metadata randomness
talevy Jul 17, 2018
cc969d1
cleanup bulk write routing IT test and remove Unit tests for this
talevy Jul 18, 2018
284d3ee
add randomization to bulkintegrationIT test
talevy Jul 19, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,9 @@ Index[] concreteIndices(Context context, String... indexExpressions) {
AliasOrIndex.Alias alias = (AliasOrIndex.Alias) aliasOrIndex;
IndexMetaData writeIndex = alias.getWriteIndex();
if (writeIndex == null) {
if (alias.getIndices().size() > 1) {
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]");
}
throw new IllegalArgumentException("no write index is defined for alias [" + alias.getAliasName() + "]." +
" The write index may be explicitly disabled using is_write_index=false or the alias points to multiple" +
" indices without one being designated as a write index");
}
concreteIndices.add(writeIndex.getIndex());
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,21 @@

package org.elasticsearch.action.bulk;

import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
import org.elasticsearch.action.get.GetRequestBuilder;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.test.ESIntegTestCase;

import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Map;

import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath;
import static org.hamcrest.Matchers.equalTo;

public class BulkIntegrationIT extends ESIntegTestCase {
public void testBulkIndexCreatesMapping() throws Exception {
Expand All @@ -40,4 +48,51 @@ public void testBulkIndexCreatesMapping() throws Exception {
assertTrue(mappingsResponse.getMappings().get("logstash-2014.03.30").containsKey("logs"));
});
}

/**
* This tests that the {@link TransportBulkAction} evaluates alias routing values correctly when dealing with
* an alias pointing to multiple indices, while a write index exits.
*/
public void testBulkWithWriteIndexAndRouting() {
Map<String, Integer> twoShardsSettings = Collections.singletonMap(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 2);
client().admin().indices().prepareCreate("index1")
.addAlias(new Alias("alias1").indexRouting("0")).setSettings(twoShardsSettings).get();
client().admin().indices().prepareCreate("index2")
.addAlias(new Alias("alias1").indexRouting("0").writeIndex(randomFrom(false, null)))
.setSettings(twoShardsSettings).get();
client().admin().indices().prepareCreate("index3")
.addAlias(new Alias("alias1").indexRouting("1").writeIndex(true)).setSettings(twoShardsSettings).get();

IndexRequest indexRequest = new IndexRequest("index3", "type", "id");
boolean indexWithRouting = randomBoolean();
if (indexWithRouting) {
indexRequest.routing("0");
}
indexRequest.source(Collections.singletonMap("foo", "bar"));
IndexRequest indexRequestWithAlias = new IndexRequest("alias1", "type", "id");
Copy link
Contributor

Choose a reason for hiding this comment

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

can we use different ids for the different indices? I find this super confusing to reason about. Maybe also add the routing value you expect to be used to the id.

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 think it is also OK to remove the first index request entirely. I just wanted to it to demonstrate that the two calls do not interact with the same document, even though they have the same index/type/id.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On second thought, I remember the purpose of this other index request. It is to verify the other branches of the routing resolution. The reason I think it is OK to remove is that this IT test is meant to test the connection to this method, not all its branches. The Unit tests do that.

Copy link
Contributor

Choose a reason for hiding this comment

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

randomly set the routing to "1"?

indexRequestWithAlias.source(Collections.singletonMap("foo", "baz"));
BulkResponse bulkResponse = client().prepareBulk()
.add(indexRequest).add(indexRequestWithAlias).get();
assertThat(bulkResponse.getItems()[0].getResponse().getIndex(), equalTo("index3"));
assertThat(bulkResponse.getItems()[0].getResponse().getShardId().getId(), equalTo(1));
assertThat(bulkResponse.getItems()[0].getResponse().getVersion(), equalTo(1L));
assertThat(bulkResponse.getItems()[0].getResponse().status(), equalTo(RestStatus.CREATED));
assertThat(bulkResponse.getItems()[1].getResponse().getIndex(), equalTo("index3"));
assertThat(bulkResponse.getItems()[1].getResponse().getShardId().getId(), equalTo(0));
assertThat(bulkResponse.getItems()[1].getResponse().getVersion(), equalTo(1L));
assertThat(bulkResponse.getItems()[1].getResponse().status(), equalTo(RestStatus.CREATED));
GetRequestBuilder getBuilder = client().prepareGet("index3", "type", "id");
if (indexWithRouting) {
getBuilder.setRouting("0");
}
assertThat(getBuilder.get().getSource().get("foo"), equalTo("bar"));
assertThat(client().prepareGet("index3", "type", "id").setRouting("1").get().getSource().get("foo"), equalTo("baz"));

client().prepareBulk().add(client().prepareUpdate("alias1", "type", "id").setDoc("foo", "updated")).get();
Copy link
Contributor

Choose a reason for hiding this comment

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

we should check errors here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will add. didn't think this was necessary since there are get-action calls verifying success of this operation

assertThat(getBuilder.get().getSource().get("foo"), equalTo("bar"));
assertThat(client().prepareGet("index3", "type", "id").setRouting("1").get().getSource().get("foo"), equalTo("updated"));
client().prepareBulk().add(client().prepareDelete("alias1", "type", "id")).get();
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure what we're testing here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TransportBulkAction resolves routing for delete requests just as it does for index and update. This delete request is here to verify this

assertThat(getBuilder.get().getSource().get("foo"), equalTo("bar"));
assertFalse(client().prepareGet("index3", "type", "id").setRouting("1").get().isExists());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ public class TransportBulkActionTests extends ESTestCase {
private ThreadPool threadPool;

private TestTransportBulkAction bulkAction;
private TransportShardBulkAction shardBulkAction;

class TestTransportBulkAction extends TransportBulkAction {

Expand Down Expand Up @@ -162,7 +161,6 @@ public void testDeleteNonExistingDocExternalGteVersionCreatesIndex() throws Exce
}

public void testRoutingResolvesToWriteIndexAliasMetaData() throws Exception {
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we need a test at this level. We already unit test MetaData and we have an IT test to check it works end to end.

Copy link
Contributor

Choose a reason for hiding this comment

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

actually, I don't think we have an IT test, but we should

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 couldn't find an IT test that verifies this, and I thought that this level of unit test was sufficient. Will add IT test (will also double check that one doesn't exist)

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this can be removed now that we have an IT test? this layer doesn't add anything to the write index resolving as it is all in the resolver? it's just glue and is covered well by the IT?

ClusterState oldState = clusterService.state();
clusterService.submitStateUpdateTask("_update", new ClusterStateUpdateTask() {
@Override
public ClusterState execute(ClusterState currentState) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ public void testAliases() throws Exception {
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class,
() -> client().index(indexRequest("alias1").type("type1").id("1").source(source("2", "test"),
XContentType.JSON)).actionGet());
assertThat(exception.getMessage(), equalTo("Alias [alias1] points to an index [test] with [is_write_index=false]"));
assertThat(exception.getMessage(), equalTo("no write index is defined for alias [alias1]." +
" The write index may be explicitly disabled using is_write_index=false or the alias points to multiple" +
" indices without one being designated as a write index"));

logger.info("--> aliasing index [test] with [alias1]");
assertAcked(admin().indices().prepareAliases().addAlias("test", "alias1"));
Expand All @@ -116,14 +118,16 @@ public void testAliases() throws Exception {
exception = expectThrows(IllegalArgumentException.class,
() -> client().index(indexRequest("alias1").type("type1").id("1").source(source("2", "test"),
XContentType.JSON)).actionGet());
assertThat(exception.getMessage(),
equalTo("Alias [alias1] points to multiple indices with none set as a write-index [is_write_index=true]"));
assertThat(exception.getMessage(), equalTo("no write index is defined for alias [alias1]." +
" The write index may be explicitly disabled using is_write_index=false or the alias points to multiple" +
" indices without one being designated as a write index"));

logger.info("--> deleting against [alias1], should fail now");
exception = expectThrows(IllegalArgumentException.class,
() -> client().delete(deleteRequest("alias1").type("type1").id("1")).actionGet());
assertThat(exception.getMessage(),
equalTo("Alias [alias1] points to multiple indices with none set as a write-index [is_write_index=true]"));
assertThat(exception.getMessage(), equalTo("no write index is defined for alias [alias1]." +
" The write index may be explicitly disabled using is_write_index=false or the alias points to multiple" +
" indices without one being designated as a write index"));

logger.info("--> remove aliasing index [test_x] with [alias1]");
assertAcked(admin().indices().prepareAliases().removeAlias("test_x", "alias1"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1107,8 +1107,9 @@ public void testConcreteWriteIndexWithNoWriteIndexWithSingleIndex() {
new UpdateRequest("test-alias", "_type", "_id"), new DeleteRequest("test-alias"));
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class,
() -> indexNameExpressionResolver.concreteWriteIndex(state, request));
assertThat(exception.getMessage(),
equalTo("Alias [test-alias] points to an index [test-0] with [is_write_index=false]"));
assertThat(exception.getMessage(), equalTo("no write index is defined for alias [test-alias]." +
" The write index may be explicitly disabled using is_write_index=false or the alias points to multiple" +
" indices without one being designated as a write index"));
}

public void testConcreteWriteIndexWithNoWriteIndexWithMultipleIndices() {
Expand All @@ -1126,16 +1127,18 @@ public void testConcreteWriteIndexWithNoWriteIndexWithMultipleIndices() {
new UpdateRequest("test-alias", "_type", "_id"), new DeleteRequest("test-alias"));
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class,
() -> indexNameExpressionResolver.concreteWriteIndex(state, request));
assertThat(exception.getMessage(),
equalTo("Alias [test-alias] points to multiple indices with none set as a write-index [is_write_index=true]"));
assertThat(exception.getMessage(), equalTo("no write index is defined for alias [test-alias]." +
" The write index may be explicitly disabled using is_write_index=false or the alias points to multiple" +
" indices without one being designated as a write index"));
}

public void testAliasResolutionNotAllowingMultipleIndices() {
boolean test0WriteIndex = randomBoolean();
MetaData.Builder mdBuilder = MetaData.builder()
.put(indexBuilder("test-0").state(State.OPEN)
.putAlias(AliasMetaData.builder("test-alias").writeIndex(randomFrom(false, null))))
.putAlias(AliasMetaData.builder("test-alias").writeIndex(randomFrom(test0WriteIndex, null))))
.put(indexBuilder("test-1").state(State.OPEN)
.putAlias(AliasMetaData.builder("test-alias").writeIndex(randomFrom(false, null))));
.putAlias(AliasMetaData.builder("test-alias").writeIndex(randomFrom(!test0WriteIndex, null))));
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
String[] strings = indexNameExpressionResolver
.indexAliases(state, "test-0", x -> true, true, "test-*");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,14 +187,16 @@ public void testResolveIndexRouting() {
}

public void testResolveWriteIndexRouting() {
boolean aliasZeroWrite = randomBoolean();
IndexMetaData.Builder builder = IndexMetaData.builder("index")
.settings(Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT))
.numberOfShards(1)
.numberOfReplicas(0)
.putAlias(AliasMetaData.builder("alias0").build())
.putAlias(AliasMetaData.builder("alias0").writeIndex(aliasZeroWrite).build())
.putAlias(AliasMetaData.builder("alias1").routing("1").build())
Copy link
Contributor

Choose a reason for hiding this comment

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

we don't have an explicit is_write_index set to true in this test. Set it here randomly?

Copy link
Contributor Author

@talevy talevy Jul 17, 2018

Choose a reason for hiding this comment

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

I'll take a look. I didn't add it here since I didn't feel like it was this test's duty to worry about such things since getWriteIndex should be doing that test

Copy link
Contributor Author

Choose a reason for hiding this comment

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

didn't hurt to add. added

.putAlias(AliasMetaData.builder("alias2").routing("1,2").build())
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't believe we check rejection of multi value routings with is_write_index set to true. Can we add a test that it fails (preferably but rejecting it when building the metadata objects?)

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 will add a new alias4 with is_write_index=true and have it fail.

(preferably but rejecting it when building the metadata objects?)

I think this touches the previous comment about failing invalid routing values early. Although I agree, I do not feel comfortable introducing this change in this PR

Copy link
Contributor

Choose a reason for hiding this comment

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

++

.putAlias(AliasMetaData.builder("alias3").writeIndex(false).build());
.putAlias(AliasMetaData.builder("alias3").writeIndex(false).build())
.putAlias(AliasMetaData.builder("alias4").routing("1,2").writeIndex(true).build());
MetaData metaData = MetaData.builder().put(builder).build();

// no alias, no index
Expand Down Expand Up @@ -222,6 +224,9 @@ public void testResolveWriteIndexRouting() {
exception = expectThrows(IllegalArgumentException.class, () -> metaData.resolveWriteIndexRouting("1", "alias2"));
assertThat(exception.getMessage(),
is("index/alias [alias2] provided with routing value [1,2] that resolved to several routing values, rejecting operation"));
exception = expectThrows(IllegalArgumentException.class, () -> metaData.resolveWriteIndexRouting(randomFrom("1", null), "alias4"));
assertThat(exception.getMessage(),
is("index/alias [alias4] provided with routing value [1,2] that resolved to several routing values, rejecting operation"));

// alias with no write index
exception = expectThrows(IllegalArgumentException.class, () -> metaData.resolveWriteIndexRouting("1", "alias3"));
Expand All @@ -234,7 +239,7 @@ public void testResolveWriteIndexRouting() {
.settings(Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT))
.numberOfShards(1)
.numberOfReplicas(0)
.putAlias(AliasMetaData.builder("alias0").writeIndex(true).build())
.putAlias(AliasMetaData.builder("alias0").writeIndex(!aliasZeroWrite).build())
.putAlias(AliasMetaData.builder("alias1").routing("0").writeIndex(true).build())
.putAlias(AliasMetaData.builder("alias2").writeIndex(true).build());
MetaData metaDataTwoIndices = MetaData.builder(metaData).put(builder2).build();
Expand Down
22 changes: 22 additions & 0 deletions server/src/test/java/org/elasticsearch/get/GetActionIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.elasticsearch.action.get.MultiGetRequest;
import org.elasticsearch.action.get.MultiGetRequestBuilder;
import org.elasticsearch.action.get.MultiGetResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.support.DefaultShardOperationFailedException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
Expand All @@ -39,6 +40,7 @@
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.engine.VersionConflictEngineException;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.InternalSettingsPlugin;

Expand All @@ -51,6 +53,7 @@
import static java.util.Collections.singleton;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.instanceOf;
Expand Down Expand Up @@ -192,6 +195,25 @@ public void testSimpleGet() {
assertThat(response.isExists(), equalTo(false));
}

public void testGetWithAliasPointingToMultipleIndices() {
client().admin().indices().prepareCreate("index1")
.addAlias(new Alias("alias1").indexRouting("0")).get();
if (randomBoolean()) {
client().admin().indices().prepareCreate("index2")
.addAlias(new Alias("alias1").indexRouting("0").writeIndex(randomFrom(false, null))).get();
} else {
client().admin().indices().prepareCreate("index3")
.addAlias(new Alias("alias1").indexRouting("1").writeIndex(true)).get();
}
IndexResponse indexResponse = client().prepareIndex("index1", "type", "id")
.setSource(Collections.singletonMap("foo", "bar")).get();
assertThat(indexResponse.status().getStatus(), equalTo(RestStatus.CREATED.getStatus()));

IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () ->
client().prepareGet("alias1", "type", "_alias_id").get());
assertThat(exception.getMessage(), endsWith("can't execute a single index op"));
}

private static String indexOrAlias() {
return randomBoolean() ? "test" : "alias";
}
Expand Down