Skip to content

Conversation

@original-brownbear
Copy link
Contributor

@original-brownbear original-brownbear commented Dec 14, 2018

Deterministic Cluster State Tests for Snapshots

  • Single pointer to the current cluster state (TestClusterState) and DeterministicTaskQueue infrastructure to as well as no networking to be able to iterate through every step of state updates and snapshot task execution in a reproducible manner
  • Run a single successful snapshot

* Use `DeterministicTaskQueue` infrastructure to reproduce elastic#32265
@original-brownbear original-brownbear added >test Issues or PRs that are addressing/adding tests :Distributed Coordination/Snapshot/Restore Anything directly related to the `_snapshot/*` APIs v7.0.0 labels Dec 14, 2018
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-distributed

Copy link
Contributor Author

@original-brownbear original-brownbear left a comment

Choose a reason for hiding this comment

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

@ywelsch did my best to clean this up and make it readable now :) Take a look when you have some time, I think this should be much closer now to what you were envisioning :)

@Override
public ScheduledExecutorService scheduler() {
throw new UnsupportedOperationException();
return new ScheduledExecutorService() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Needed to add a dummy return here since this was used by org.elasticsearch.index.shard.IndexShard#IndexShard (only used in the constructor though for the current test, so no actual implementation necessary otherwise).

public Cancellable scheduleWithFixedDelay(Runnable command, TimeValue interval, String executor) {
throw new UnsupportedOperationException();
// TODO: Implement fully like schedule
return new Cancellable() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added dummy return only here for now. This is used to schedule a task org.elasticsearch.indices.IndexingMemoryController.ShardsIndicesStatusChecker in org.elasticsearch.indices.IndexingMemoryController. Just a dummy for now sine that task doesn't seem relevant for this test.

Copy link
Contributor

Choose a reason for hiding this comment

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

might as well properly implement this, looks not that difficult (I think it's just to call super. scheduleWithFixedDelay() here) and add a test to DeterministicTaskQueueTests.

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 in 2fa91b3.

}
};

TestClusterNode(DiscoveryNode node, DeterministicTaskQueue deterministicTaskQueue) throws IOException {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Setting up all the real services used for snapshotting with here with the exceptions:

  • MockTransportService that just short-circuits the network.
  • Mock ClusterStatePublisher that just short-circuits the network (I added a todo here, because I wasn't sure if we could maybe use the real thing here. It seemed very tricky to do so, but maybe it's not or worth the effort?)

new IndexScopedSettings(settings, IndexScopedSettings.BUILT_IN_INDEX_SETTINGS);
indicesService = new IndicesService(
settings,
mock(PluginsService.class),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just mocked this one out since it's not relevant for the test and just a bunch of code to get up and running.

shardStateAction,
new NodeMappingRefreshAction(transportService, new MetaDataMappingService(clusterService, indicesService)),
repositoriesService,
mock(SearchService.class),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just mocked this one out since it's not relevant for the test and just a bunch of code to get up and running.


private final ClusterService clusterService;

private final RepositoriesService repositoriesService = mock(RepositoriesService.class);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just mocked this one out since it's not that relevant for the test (we really only need it to return the repository, that's the only call we make to it) and just a bunch of code to get up and running.

)
);

runOutstandingTasks();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Running all tasks so that the index is fully set up when we create the snapshot.

@original-brownbear
Copy link
Contributor Author

@ywelsch fixed all issues we talk about today in e719046:

  • Single task queue for all nodes
  • Real repositories service
  • Only a single call to run all tasks in the queue, chaining everything else via callbacks
  • Run non-blocking transport actions in the task queue (I simply filtered for recovery actions here, since multiple of these were blocking)
    • The fact that those block lead to me still having to update the state in sync on all nodes, see e719046#diff-8216ad578b188d436b9af3c14b23a9d9R498 because we don't have a way of handling the delayed recovery exception that leads to scheduling these blocking actions with a delay in the recovery logic.

@original-brownbear
Copy link
Contributor Author

Jenkins run gradle build tests 2

Copy link
Contributor

@ywelsch ywelsch left a comment

Choose a reason for hiding this comment

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

Looks very good already

repositoriesService = new RepositoriesService(
settings, clusterService, transportService,
Collections.singletonMap(FsRepository.TYPE, metaData -> {
final Repository repository = new FsRepository(metaData, createEnvironment(), xContentRegistry()) {
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 confused. With each node having their own environment, how do the nodes access a shared FS location for writing the snapshot?

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 the reason this did not fail yet is that we aren't writing any data yet because all the shards are empty?

Regardless, I cleaned this up and made sure all nodes have the same repository path in their settings now :)

@original-brownbear
Copy link
Contributor Author

@ywelsch thanks for taking a look! All points addressed I think -> should be good for another review.

tempDir = createTempDir();
deterministicTaskQueue =
new DeterministicTaskQueue(Settings.builder().put(NODE_NAME_SETTING.getKey(), "shared").build(), random());
// TODO: Random number of master nodes and simulate master failover states
Copy link
Contributor

Choose a reason for hiding this comment

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

we're not simulating failovers yet?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, not yet. I was under the impression that we wanted to get the simple successful test case in first and then add those things when we last spoke about the steps here?

Copy link
Contributor

Choose a reason for hiding this comment

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

yes, I found the comment just confusing here, given that we have no master failovers yet.

final ClusterSettings clusterSettings = new ClusterSettings(settings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
final ThreadPool threadPool = deterministicTaskQueue.getThreadPool();
clusterService = new ClusterService(settings, clusterSettings, threadPool, masterService);
mockTransport = new MockTransport() {
Copy link
Contributor

Choose a reason for hiding this comment

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

perhaps it's simpler to implement DisruptableMockTransport, see CoordinatorTests

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 much nicer :), done in 1544b8a

// Mock publisher that invokes other cluster change listeners directly
// TODO: Run state updates on the individual nodes out of order, this is currently not possible
// TODO: because it can lead to running the blocking recovery tasks on the deterministicTaskQueue
// TODO: when a DelayRecoveryException is thrown on the transport layer as a result of
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 understand, the problem is not the DelayRecoveryException, but the general blocking nature of peer recoveries (e.g. PeerRecoveryTargetService blockingly waits on the recovery to complete).

Perhaps we could only have this while allocating shards, but for the duration of the snapshot, while no shards are being allocated, revert to a more randomized mode. Alternatively, we can test without replica shards for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed the replicas for now in 237f9e7, that also allows for a simpler mock transport until we have non blocking replication.

});
});
masterService.setClusterStateSupplier(currentState::get);
if (node.isMasterNode()) {
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 if-clause necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed in 7259b45

public Cancellable scheduleWithFixedDelay(Runnable command, TimeValue interval, String executor) {
throw new UnsupportedOperationException();
// TODO: Implement fully like schedule
return new Cancellable() {
Copy link
Contributor

Choose a reason for hiding this comment

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

might as well properly implement this, looks not that difficult (I think it's just to call super. scheduleWithFixedDelay() here) and add a test to DeterministicTaskQueueTests.

@original-brownbear
Copy link
Contributor Author

@ywelsch all points addressed :)

Copy link
Contributor

@ywelsch ywelsch left a comment

Choose a reason for hiding this comment

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

LGTM

tempDir = createTempDir();
deterministicTaskQueue =
new DeterministicTaskQueue(Settings.builder().put(NODE_NAME_SETTING.getKey(), "shared").build(), random());
// TODO: Random number of master nodes and simulate master failover states
Copy link
Contributor

Choose a reason for hiding this comment

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

yes, I found the comment just confusing here, given that we have no master failovers yet.

@original-brownbear
Copy link
Contributor Author

@ywelsch thanks!

@original-brownbear original-brownbear merged commit 85be9d6 into elastic:master Dec 31, 2018
@original-brownbear original-brownbear deleted the deterministic-snapshot-tests branch December 31, 2018 10:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

:Distributed Coordination/Snapshot/Restore Anything directly related to the `_snapshot/*` APIs >test Issues or PRs that are addressing/adding tests v7.0.0-beta1

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants