Skip to content

Conversation

@original-brownbear
Copy link
Contributor

  • Move to file based discovery in AbstractDisruptionTestCase
    • Don't use the ordinals of the unicast hosts at config time, use them when actually starting the cluster and only write the nodes whose id is in the unicast host array to the discovery file
  • Remove all code paths in ClusterDiscoveryConfiguration that become unused due to not using it for configuration in AbstractDisruptionTestCase anymore
  • Relates Fix port assignment and discovery in tests #33675

@original-brownbear original-brownbear added >test Issues or PRs that are addressing/adding tests v7.0.0 :Distributed Coordination/Cluster Coordination Cluster formation and cluster state publication, including cluster membership and fault detection. v6.4.0 labels Oct 15, 2018
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-distributed

@original-brownbear
Copy link
Contributor Author

@DaveCTurner take a look when you have a minute, hope this is in the direction of what we discussed last week :)

@jasontedor jasontedor added v6.5.0 and removed v6.4.0 labels Oct 15, 2018
@DaveCTurner
Copy link
Contributor

Yes, this is the right sort of direction to take. However, the crucial change that we're looking for is to make it so that ClusterDiscoveryConfiguration no longer selects a free port by binding to it, releasing it, then fixing it in the config file of the corresponding node, because this is what leads to our test failures.

I expect that if you make this change then a lot more of this machinery will turn out no longer to be needed, because ESIntegTestCase already allows for nodes to bind to any port and we don't find out which one until much later.

Another possible improvement would be for InternalTestCluster to become responsible for unicastHostsOrdinals, since it's responsible for writing the discovery file, and avoid passing it around when starting the cluster as we do today. In all existing tests we either set it to null or new int[]{0} so I think we could just use a boolean field, say hostsListContainsOnlyFirstNode, to reflect more clearly that we're not covering many cases here.

@original-brownbear
Copy link
Contributor Author

original-brownbear commented Oct 15, 2018

@DaveCTurner

Yes, this is the right sort of direction to take. However, the crucial change that we're looking for is to make it so that ClusterDiscoveryConfiguration no longer selects a free port by binding to it, releasing it, then fixing it in the config file of the corresponding node, because this is what leads to our test failures.

See below, this fixes the situation for the AbstractDisruptionTestCase but you're right that it leaves one use of this port binding approach.

I expect that if you make this change then a lot more of this machinery will turn out no longer to be needed, because ESIntegTestCase already allows for nodes to bind to any port and we don't find out which one until much later.

I deleted what is not used anymore in here already. The only remaining use of ClusterDiscoveryConfiguration is org.elasticsearch.test.SecuritySettingsSource which extends it.
Should I try to remove that in the is PR as well?

Another possible improvement would be for InternalTestCluster to become responsible for unicastHostsOrdinals, since it's responsible for writing the discovery file, and avoid passing it around when starting the cluster as we do today. In all existing tests we either set it to null or new int[]{0} so I think we could just use a boolean field, say hostsListContainsOnlyFirstNode, to reflect more clearly that we're not covering many cases here.

Sounds good will do :)

@original-brownbear
Copy link
Contributor Author

@DaveCTurner hmm with the changes in here the only user of the old port assignment is the calls from SecuritySettingsSource but they never made use of the facilities to only set a subset of hosts as unicast hosts anyway.
Can't I just remove all the code for finding the port and setting the unicast host list setting, set the discovery to file based in those tests and it's fine because the internal cluster will just automatically configure all the hosts in the discovery file?

@DaveCTurner
Copy link
Contributor

Can't I just remove all the code for finding the port and setting the unicast host list setting, set the discovery to file based in those tests and it's fine because the internal cluster will just automatically configure all the hosts in the discovery file?

Yes, except for DiscoveryDisruptionIT which needs each discovery file to only refer to the first node.

The only remaining use of ClusterDiscoveryConfiguration is org.elasticsearch.test.SecuritySettingsSource which extends it.

I wonder if SecuritySettingsSource should just extend NodeConfigurationSource directly. Needs a bit of study to look at how it's being used, but that's what I'd try.

@original-brownbear
Copy link
Contributor Author

Yes, except for DiscoveryDisruptionIT which needs each discovery file to only refer to the first node.

Yea but that's not using this code anymore anyway with this change :)

I wonder if SecuritySettingsSource should just extend NodeConfigurationSource directly. Needs a bit of study to look at how it's being used, but that's what I'd try.

It's the only thing that extends ClusterDiscoveryConfiguration so we could just flatten that I think yes.

@DaveCTurner
Copy link
Contributor

DaveCTurner commented Oct 15, 2018

Yea but that's not using this code anymore anyway with this change :)

Good point, I hadn't spotted that.

NB I think hostsListContainsOnlyFirstNode can be a field on InternalTestCluster that you set to true only in DiscoveryDisruptionIT, avoiding all the overloaded methods that are needed just to pass it in.

@original-brownbear
Copy link
Contributor Author

NB I think hostsListContainsOnlyFirstNode can be a field on InternalTestCluster that you set to true only in DiscoveryDisruptionIT, avoiding all the overloaded constructors that are needed just to pass it in.

Sure that seems much nicer :) => on it

…now, ClusterDiscoveryConfiguration doesn't do port assignments anymore
@original-brownbear
Copy link
Contributor Author

@DaveCTurner alright done.

Port assignments don't happen in ClusterDiscoveryConfiguration anymore and I made the hostsListContainsOnlyFirstNode a field in the internal test cluster :)

Copy link
Contributor

@DaveCTurner DaveCTurner left a comment

Choose a reason for hiding this comment

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

I suggested a few more simplifications that I think are worth investigating.

List<String> startCluster(int numberOfNodes, int minimumMasterNode, boolean hostsListContainsOnlyFirstNode) {
configureCluster(numberOfNodes, minimumMasterNode);
InternalTestCluster internalCluster = internalCluster();
internalCluster.setHostsListContainsOnlyFirstNode(hostsListContainsOnlyFirstNode);
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 call can completely move to DiscoveryDisruptionIT, removing another overload:

    public void testUnicastSinglePingResponseContainsMaster() throws Exception {
        internalCluster().setHostsListContainsOnlyFirstNode(true);
        List<String> nodes = startCluster(4, -1);

etc...

private NodeConfigurationSource discoveryConfig;

@Override
protected Settings nodeSettings(int nodeOrdinal) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would like to see if it's possible to pass in all the settings by implementing this method directly, rather than using the NodeConfigurationSource etc.

For instance it looks like these settings could be here:

            .put(FaultDetection.PING_TIMEOUT_SETTING.getKey(), "1s") // for hitting simulated network failures quickly
            .put(FaultDetection.PING_RETRIES_SETTING.getKey(), "1") // for hitting simulated network failures quickly
            .put("discovery.zen.join_timeout", "10s")  // still long to induce failures but to long so test won't time out
            .put(DiscoverySettings.PUBLISH_TIMEOUT_SETTING.getKey(), "1s") // <-- for hitting simulated network failures quickly
            .put(TransportService.TCP_CONNECT_TIMEOUT.getKey(), "10s") // Network delay disruption waits for the min between this
//and
                .put(TestZenDiscovery.USE_MOCK_PINGS.getKey(), false).build();

I also think these aren't needed and we can just make use of the machinery in ESIntegTestCase to set them appropriately:

                .put(NodeEnvironment.MAX_LOCAL_STORAGE_NODES_SETTING.getKey(), numberOfNodes)
                .put(ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING.getKey(), minimumMasterNode)
                .putList(DISCOVERY_HOSTS_PROVIDER_SETTING.getKey(), "file")

We use non-default settings in very few places; moving the affected tests into their own fixtures so they can override nodeSettings themselves seems worth investigating.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@DaveCTurner Let me try :) What makes this a little tricky (requiring more changes maybe?) is that we have this code in org.elasticsearch.discovery.ClusterDisruptionIT#testSearchWithRelocationAndSlowClusterStateProcessing

    /**
     * This test creates a scenario where a primary shard (0 replicas) relocates and is in POST_RECOVERY on the target
     * node but already deleted on the source node. Search request should still work.
     */
    public void testSearchWithRelocationAndSlowClusterStateProcessing() throws Exception {
        // don't use DEFAULT settings (which can cause node disconnects on a slow CI machine)
        configureCluster(Settings.EMPTY, 3, 1);

to override these defaults (though this could just be me not being so fluent in this code yet :)). Maybe do this in a follow up PR?

Copy link
Contributor

Choose a reason for hiding this comment

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

Right, that's one of the tests that I think could be in its own fixture since it needs different cluster settings.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@DaveCTurner wanna handle this here or can we move that to the next PR? (this one is already doing quite a few things)

Copy link
Contributor

Choose a reason for hiding this comment

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

Sure, a follow-up is ok.

) throws ExecutionException, InterruptedException {
void configureCluster(Settings settings, int numberOfNodes, int minimumMasterNode) {
if (minimumMasterNode < 0) {
minimumMasterNode = numberOfNodes / 2 + 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

As far as I can tell, we allow tests to set minimumMasterNode to the "wrong" value only to allow dynamically adding nodes after the cluster has started. If so, it'd be better to allow ESIntegTestCase to manage this number for us.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@DaveCTurner Aren't we only ever passing -1 here from org.elasticsearch.discovery.AbstractDisruptionTestCase#startCluster(int, int) when we start the cluster? It seems like this is just a convenience method for not having to set a value by hand here?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, let me clarify. I do not think this method should receive an explicit minimumMasterNode value at all, whether -1 or otherwise. In almost all cases, ESIntegTestCase now does a better job of automatically managing this setting than is done here. It was, however, possible that some of the tests were using this feature to set this value to something other than numberOfNodes / 2 + 1, for instance:

In the first two cases this is actually correct because numberOfNodes also includes some data-only nodes. The last case is kinda strange - I think it really can form two clusters, and I'm not sure what it's actually testing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@DaveCTurner given the short exchange in Slack, wanna keep this around for now maybe?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, let's do this in a follow-up. NB the action to take here is to make this a cluster with 1 master node and 1 data node, which'd then mean that we can fall back on ESIntegTestCase's management of the cluster configuration.

private ServiceDisruptionScheme activeDisruptionScheme;
private Function<Client, Client> clientWrapper;

// If set to tru only the first node in the cluster will be made a unicast node
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: s/tru/true/

private static int nextPort = calcBasePort();

private final int[] unicastHostOrdinals;
private final int[] unicastHostPorts;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this used any more? I think it can go away.

@original-brownbear
Copy link
Contributor Author

@DaveCTurner all comments addressed I hope :) can you take another look? :)

*/
public SecuritySettingsSource(int numOfNodes, boolean sslEnabled, Path parentFolder, Scope scope) {
super(numOfNodes, DEFAULT_SETTINGS);
this.nodeSettings = Settings.builder().put(Settings.EMPTY).put(DEFAULT_SETTINGS).build();
Copy link
Contributor

Choose a reason for hiding this comment

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

.put(Settings.EMPTY) is a no-op. Also, public static final Settings DEFAULT_SETTINGS = Settings.EMPTY; means that .put(DEFAULT_SETTINGS) is also a no-op. I think this means that both nodeSettings and transportClientSettings are just Settings.EMPTY (and this means there are more no-ops elsewhere).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right ... sorry for missing that :) Removed fields and their noop use.

Copy link
Contributor

@DaveCTurner DaveCTurner left a comment

Choose a reason for hiding this comment

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

I asked for three minor changes, then this is good to go.

public Settings transportClientSettings() {
Settings superSettings = super.transportClientSettings();
Settings superSettings = Settings.EMPTY;
Settings.Builder builder = Settings.builder().put(superSettings);
Copy link
Contributor

Choose a reason for hiding this comment

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

I would inline superSettings and now there's another put(Settings.EMPTY) to nuke.

) throws ExecutionException, InterruptedException {
void configureCluster(Settings settings, int numberOfNodes, int minimumMasterNode) {
if (minimumMasterNode < 0) {
minimumMasterNode = numberOfNodes / 2 + 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, let's do this in a follow-up. NB the action to take here is to make this a cluster with 1 master node and 1 data node, which'd then mean that we can fall back on ESIntegTestCase's management of the cluster configuration.

private NodeConfigurationSource discoveryConfig;

@Override
protected Settings nodeSettings(int nodeOrdinal) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Sure, a follow-up is ok.

public void testUnicastSinglePingResponseContainsMaster() throws Exception {
List<String> nodes = startCluster(4, -1, new int[]{0});
List<String> nodes = startCluster(4, -1);
internalCluster().setHostsListContainsOnlyFirstNode(true);
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 has to happen before you start the cluster, or else the cluster will start with full knowledge.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right fixing

public void testIsolatedUnicastNodes() throws Exception {
List<String> nodes = startCluster(4, -1, new int[]{0});
List<String> nodes = startCluster(4, -1);
internalCluster().setHostsListContainsOnlyFirstNode(true);
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 has to happen before you start the cluster, or else the cluster will start with full knowledge.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right fixing

@original-brownbear
Copy link
Contributor Author

@DaveCTurner all addressed in 5503164 I think:)

Copy link
Contributor

@DaveCTurner DaveCTurner left a comment

Choose a reason for hiding this comment

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

LGTM, thanks @original-brownbear. I added this PR and the followup we discussed to the list in #33675.

@original-brownbear original-brownbear merged commit ea576a8 into elastic:master Oct 16, 2018
@original-brownbear original-brownbear deleted the 33675 branch October 16, 2018 14:28
@original-brownbear
Copy link
Contributor Author

@DaveCTurner thanks for the review!

kcm pushed a commit that referenced this pull request Oct 30, 2018
* Discovery: Move AbstractDisruptionTestCase to file-based discovery.
* Relates #33675
* Simplify away ClusterDiscoveryConfiguration
original-brownbear added a commit to original-brownbear/elasticsearch that referenced this pull request Nov 16, 2018
* Discovery: Move AbstractDisruptionTestCase to file-based discovery.
* Relates elastic#33675
* Simplify away ClusterDiscoveryConfiguration
original-brownbear added a commit that referenced this pull request Nov 16, 2018
* Discovery: Move AbstractDisruptionTestCase to file-based discovery.
* Relates #33675
* Simplify away ClusterDiscoveryConfiguration
@jimczi jimczi added v7.0.0-beta1 and removed v7.0.0 labels Feb 7, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

:Distributed Coordination/Cluster Coordination Cluster formation and cluster state publication, including cluster membership and fault detection. >test Issues or PRs that are addressing/adding tests v6.5.0 v7.0.0-beta1

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants