-
Notifications
You must be signed in to change notification settings - Fork 25k
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
Added CCR rolling upgrade tests #36648
Changes from 13 commits
af67a13
3ba5d9a
ff096c7
1f242fd
52cb061
fa4b45d
f250c0f
0f9b9f4
ca10738
d186232
4145476
99d53a2
72d25d8
85371ed
78754af
c5b8071
62461ef
f554102
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 |
---|---|---|
@@ -0,0 +1,292 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
package org.elasticsearch.upgrades; | ||
|
||
import org.apache.http.util.EntityUtils; | ||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
import org.elasticsearch.Version; | ||
import org.elasticsearch.client.Request; | ||
import org.elasticsearch.client.Response; | ||
import org.elasticsearch.common.settings.Settings; | ||
import org.elasticsearch.common.xcontent.ObjectPath; | ||
import org.elasticsearch.common.xcontent.XContentHelper; | ||
import org.elasticsearch.common.xcontent.json.JsonXContent; | ||
|
||
import java.io.IOException; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import static org.hamcrest.Matchers.equalTo; | ||
|
||
public class CCRIT extends AbstractUpgradeTestCase { | ||
|
||
private static final Logger LOGGER = LogManager.getLogger(CCRIT.class); | ||
|
||
private static final Version UPGRADE_FROM_VERSION = | ||
Version.fromString(System.getProperty("tests.upgrade_from_version")); | ||
|
||
private static final boolean SECOND_ROUND = "false".equals(System.getProperty("tests.first_round")); | ||
|
||
@Override | ||
protected boolean preserveClusterSettings() { | ||
return true; | ||
} | ||
|
||
public void testIndexFollowing() throws Exception { | ||
assumeTrue("CCR became available in 6.5 and test relies on a fix that was shipped with 6.5.4", | ||
UPGRADE_FROM_VERSION.onOrAfter(Version.V_6_5_4)); | ||
setupRemoteCluster(); | ||
|
||
final String leaderIndex = "my-leader-index"; | ||
final String followerIndex = "my-follower-index"; | ||
|
||
switch (CLUSTER_TYPE) { | ||
case OLD: | ||
Settings indexSettings = Settings.builder() | ||
.put("index.soft_deletes.enabled", true) | ||
.put("index.number_of_shards", 1) | ||
.build(); | ||
createIndex(leaderIndex, indexSettings); | ||
followIndex(leaderIndex, followerIndex); | ||
index(leaderIndex, "1"); | ||
assertDocumentExists(leaderIndex, "1"); | ||
assertBusy(() -> { | ||
assertFollowerGlobalCheckpoint(followerIndex, 0); | ||
assertDocumentExists(followerIndex, "1"); | ||
}); | ||
break; | ||
case MIXED: | ||
if (SECOND_ROUND == false) { | ||
index(leaderIndex, "2"); | ||
assertDocumentExists(leaderIndex, "2"); | ||
assertBusy(() -> { | ||
assertFollowerGlobalCheckpoint(followerIndex, 1); | ||
assertDocumentExists(followerIndex, "2"); | ||
}); | ||
} else { | ||
index(leaderIndex, "3"); | ||
assertDocumentExists(leaderIndex, "3"); | ||
assertBusy(() -> { | ||
assertFollowerGlobalCheckpoint(followerIndex, 2); | ||
assertDocumentExists(followerIndex, "3"); | ||
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. nit: can we also verify doc1, and doc2 |
||
}); | ||
} | ||
break; | ||
case UPGRADED: | ||
index(leaderIndex, "4"); | ||
assertDocumentExists(leaderIndex, "4"); | ||
assertBusy(() -> { | ||
assertFollowerGlobalCheckpoint(followerIndex, 3); | ||
assertDocumentExists(followerIndex, "4"); | ||
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. nit: can we also verify doc1, doc2, and doc3? |
||
}); | ||
pauseFollow(followerIndex); | ||
closeIndex(followerIndex); | ||
unfollow(followerIndex); | ||
break; | ||
default: | ||
throw new UnsupportedOperationException("Unknown cluster type [" + CLUSTER_TYPE + "]"); | ||
} | ||
} | ||
|
||
public void testAutoFollowing() throws Exception { | ||
assumeTrue("CCR became available in 6.5 and test relies on a fix that was shipped with 6.5.4", | ||
UPGRADE_FROM_VERSION.onOrAfter(Version.V_6_5_4)); | ||
setupRemoteCluster(); | ||
|
||
final Settings indexSettings = Settings.builder() | ||
.put("index.soft_deletes.enabled", true) | ||
.put("index.number_of_shards", 1) | ||
.build(); | ||
|
||
String leaderIndex1 = "logs-20200101"; | ||
String leaderIndex2 = "logs-20200102"; | ||
String leaderIndex3 = "logs-20200103"; | ||
|
||
switch (CLUSTER_TYPE) { | ||
case OLD: | ||
putAutoFollowPattern("test_pattern"); | ||
createIndex(leaderIndex1, indexSettings); | ||
index(leaderIndex1, "1"); | ||
assertBusy(() -> { | ||
String followerIndex = "copy-" + leaderIndex1; | ||
assertNumberOfSuccessfulFollowedIndices(1); | ||
assertFollowerGlobalCheckpoint(followerIndex, 0); | ||
assertDocumentExists(followerIndex, "1"); | ||
}); | ||
break; | ||
case MIXED: | ||
if (SECOND_ROUND == false) { | ||
index(leaderIndex1, "2"); | ||
assertBusy(() -> { | ||
String followerIndex = "copy-" + leaderIndex1; | ||
assertFollowerGlobalCheckpoint(followerIndex, 1); | ||
assertDocumentExists(followerIndex, "2"); | ||
}); | ||
// Auto follow stats are kept in-memory on master elected node | ||
// and if this node get updated then auto follow stats are reset | ||
int previousNumberOfSuccessfulFollowedIndices = getNumberOfSuccessfulFollowedIndices(); | ||
createIndex(leaderIndex2, indexSettings); | ||
index(leaderIndex2, "1"); | ||
assertBusy(() -> { | ||
String followerIndex = "copy-" + leaderIndex2; | ||
assertNumberOfSuccessfulFollowedIndices(previousNumberOfSuccessfulFollowedIndices + 1); | ||
assertFollowerGlobalCheckpoint(followerIndex, 0); | ||
assertDocumentExists(followerIndex, "1"); | ||
}); | ||
} else { | ||
index(leaderIndex1, "3"); | ||
assertBusy(() -> { | ||
String followerIndex = "copy-" + leaderIndex1; | ||
assertFollowerGlobalCheckpoint(followerIndex, 2); | ||
assertDocumentExists(followerIndex, "3"); | ||
}); | ||
index(leaderIndex2, "2"); | ||
assertBusy(() -> { | ||
String followerIndex = "copy-" + leaderIndex2; | ||
assertFollowerGlobalCheckpoint(followerIndex, 1); | ||
assertDocumentExists(followerIndex, "2"); | ||
}); | ||
|
||
// Auto follow stats are kept in-memory on master elected node | ||
// and if this node get updated then auto follow stats are reset | ||
int previousNumberOfSuccessfulFollowedIndices = getNumberOfSuccessfulFollowedIndices(); | ||
createIndex(leaderIndex3, indexSettings); | ||
index(leaderIndex3, "1"); | ||
assertBusy(() -> { | ||
String followerIndex = "copy-" + leaderIndex3; | ||
assertNumberOfSuccessfulFollowedIndices(previousNumberOfSuccessfulFollowedIndices + 1); | ||
assertFollowerGlobalCheckpoint(followerIndex, 0); | ||
assertDocumentExists(followerIndex, "1"); | ||
}); | ||
} | ||
break; | ||
case UPGRADED: | ||
index(leaderIndex1, "4"); | ||
assertBusy(() -> { | ||
String followerIndex = "copy-" + leaderIndex1; | ||
assertFollowerGlobalCheckpoint(followerIndex, 3); | ||
assertDocumentExists(followerIndex, "4"); | ||
}); | ||
index(leaderIndex2, "3"); | ||
assertBusy(() -> { | ||
String followerIndex = "copy-" + leaderIndex2; | ||
assertFollowerGlobalCheckpoint(followerIndex, 2); | ||
assertDocumentExists(followerIndex, "3"); | ||
}); | ||
index(leaderIndex3, "2"); | ||
assertBusy(() -> { | ||
String followerIndex = "copy-" + leaderIndex3; | ||
assertFollowerGlobalCheckpoint(followerIndex, 1); | ||
assertDocumentExists(followerIndex, "2"); | ||
}); | ||
|
||
deleteAutoFollowPattern("test_pattern"); | ||
|
||
pauseFollow("copy-" + leaderIndex1); | ||
closeIndex("copy-" + leaderIndex1); | ||
unfollow("copy-" + leaderIndex1); | ||
|
||
pauseFollow("copy-" + leaderIndex2); | ||
closeIndex("copy-" + leaderIndex2); | ||
unfollow("copy-" + leaderIndex2); | ||
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. Maybe do all pause, close and unfollow in a single method? |
||
|
||
pauseFollow("copy-" + leaderIndex3); | ||
closeIndex("copy-" + leaderIndex3); | ||
unfollow("copy-" + leaderIndex3); | ||
break; | ||
default: | ||
throw new UnsupportedOperationException("Unknown cluster type [" + CLUSTER_TYPE + "]"); | ||
} | ||
} | ||
|
||
private static void followIndex(String leaderIndex, String followIndex) throws IOException { | ||
final Request request = new Request("PUT", "/" + followIndex + "/_ccr/follow"); | ||
request.setJsonEntity("{\"remote_cluster\": \"local\", \"leader_index\": \"" + leaderIndex + | ||
"\", \"read_poll_timeout\": \"10ms\"}"); | ||
assertOK(client().performRequest(request)); | ||
} | ||
|
||
private static void pauseFollow(String followIndex) throws IOException { | ||
assertOK(client().performRequest(new Request("POST", "/" + followIndex + "/_ccr/pause_follow"))); | ||
} | ||
|
||
private static void unfollow(String followIndex) throws IOException { | ||
assertOK(client().performRequest(new Request("POST", "/" + followIndex + "/_ccr/unfollow"))); | ||
} | ||
|
||
private static void putAutoFollowPattern(String patternName) throws IOException { | ||
Request request = new Request("PUT", "/_ccr/auto_follow/" + patternName); | ||
request.setJsonEntity("{\"leader_index_patterns\": [\"logs-*\"], \"remote_cluster\": \"local\"," + | ||
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. It's easier to follow if we pass the pattern (that's |
||
"\"follow_index_pattern\": \"copy-{{leader_index}}\", \"read_poll_timeout\": \"10ms\"}"); | ||
assertOK(client().performRequest(request)); | ||
} | ||
|
||
private static void deleteAutoFollowPattern(String patternName) throws IOException { | ||
Request request = new Request("DELETE", "/_ccr/auto_follow/" + patternName); | ||
assertOK(client().performRequest(request)); | ||
} | ||
|
||
private static void index(String index, String id) throws IOException { | ||
Request request = new Request("POST", "/" + index + "/_doc/" + id); | ||
request.setJsonEntity("{}"); | ||
assertOK(client().performRequest(request)); | ||
} | ||
|
||
private static void assertDocumentExists(String index, String id) throws IOException { | ||
Request request = new Request("HEAD", "/" + index + "/_doc/" + id); | ||
Response response = client().performRequest(request); | ||
assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); | ||
} | ||
|
||
private static void setupRemoteCluster() throws IOException { | ||
Request request = new Request("GET", "/_nodes"); | ||
Map<?, ?> nodesResponse = (Map<?, ?>) toMap(client().performRequest(request)).get("nodes"); | ||
// Select node info of first node (we don't know the node id): | ||
nodesResponse = (Map<?, ?>) nodesResponse.get(nodesResponse.keySet().iterator().next()); | ||
String transportAddress = (String) nodesResponse.get("transport_address"); | ||
|
||
LOGGER.info("Configuring local remote cluster [{}]", transportAddress); | ||
request = new Request("PUT", "/_cluster/settings"); | ||
request.setJsonEntity("{\"persistent\": {\"cluster.remote.local.seeds\": \"" + transportAddress + "\"}}"); | ||
assertThat(client().performRequest(request).getStatusLine().getStatusCode(), equalTo(200)); | ||
} | ||
|
||
private int getNumberOfSuccessfulFollowedIndices() throws IOException { | ||
Request statsRequest = new Request("GET", "/_ccr/stats"); | ||
Map<?, ?> response = toMap(client().performRequest(statsRequest)); | ||
Integer actualSuccessfulFollowedIndices = ObjectPath.eval("auto_follow_stats.number_of_successful_follow_indices", response); | ||
if (actualSuccessfulFollowedIndices != null) { | ||
return actualSuccessfulFollowedIndices; | ||
} else { | ||
return -1; | ||
} | ||
} | ||
|
||
private void assertNumberOfSuccessfulFollowedIndices(int expectedNumberOfSuccessfulFollowedIndices) throws IOException { | ||
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. Maybe remove this method and assert the result of "getNumberOfSuccessfulFollowedIndices"? |
||
Request statsRequest = new Request("GET", "/_ccr/stats"); | ||
Map<?, ?> response = toMap(client().performRequest(statsRequest)); | ||
LOGGER.info("AUTO FOLLOW STATS={}", response.get("auto_follow_stats")); | ||
Integer actualSuccessfulFollowedIndices = ObjectPath.eval("auto_follow_stats.number_of_successful_follow_indices", response); | ||
assertThat(actualSuccessfulFollowedIndices, equalTo(expectedNumberOfSuccessfulFollowedIndices)); | ||
} | ||
|
||
private void assertFollowerGlobalCheckpoint(String followerIndex, int expectedFollowerCheckpoint) throws IOException { | ||
Request statsRequest = new Request("GET", "/" + followerIndex + "/_ccr/stats"); | ||
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 a seq_no stats of the following index would give us a stronger confidence than the ccr stats. |
||
Map<?, ?> response = toMap(client().performRequest(statsRequest)); | ||
LOGGER.info("FOLLOW STATS={}", response); | ||
assertThat(((List) response.get("indices")).size(), equalTo(1)); | ||
String index = ObjectPath.eval("indices.0.index", response); | ||
assertThat(index, equalTo(followerIndex)); | ||
Integer actualFollowerCheckpoint = ObjectPath.eval("indices.0.shards.0.follower_global_checkpoint", response); | ||
assertThat(actualFollowerCheckpoint, equalTo(expectedFollowerCheckpoint)); | ||
} | ||
|
||
private static Map<String, Object> toMap(Response response) throws IOException { | ||
return XContentHelper.convertToMap(JsonXContent.jsonXContent, EntityUtils.toString(response.getEntity()), false); | ||
} | ||
|
||
} |
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 assert the existence of both doc1 and doc2? You can extend
assertDocumentExists
to accept varargs.