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

Adds tombstones to cluster state for index deletions #17265

Closed

Conversation

abeyad
Copy link

@abeyad abeyad commented Mar 23, 2016

Previously, we would determine index deletes in the cluster state by
comparing the index metadatas between the current cluster state and the
previous cluster state and decipher which ones were missing (the missing
ones are deleted indices). This led to a situation where a node that went
offline and rejoined the cluster could potentially cause dangling indices to
be imported which should have been deleted, because when a node rejoins,
its previous cluster state does not contain reliable state.

This commit introduces the notion of index tombstones in the cluster
state, where we are explicit about which indices have been deleted.
In the case where the previous cluster state is not useful for index metadata
comparisons, a node now determines which indices are to be deleted based
on these tombstones in the cluster state. There is also functionality to
purge the tombstones after exceeding a certain amount.

Closes #16358
Closes #17435

@abeyad
Copy link
Author

abeyad commented Mar 23, 2016

@bleskes @ywelsch Its a WIP, still have some functionality to implement and tests to write, but just in case you want to give a quick glance beforehand to see if its on the right track.

@abeyad abeyad force-pushed the feature/tombstone-deleted-indices branch from 494bf97 to 44d8f05 Compare March 23, 2016 03:51
IndexTombstone tombstone = cursor.value;
// we should only try to delete indices that have tombstones added since
// the last time we processed cluster state
if (tombstone.getClusterVersion() > previousVersion) {
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 we should compare the previous tombstone and the current one and generate a delta. Don't try to be overly smart.

@bleskes
Copy link
Contributor

bleskes commented Mar 23, 2016

Hi @abeyad . Thanks for picking it up. I think this can be done in a single tombstone class which is basically a queue of deleted Index object. new entries are always added at the end. Trimming is always done at the beginning. Every time you add an entry the class automatically captures the current time (both in millis and in nanos) and add it to an internal key class. Internally we can assert semantics like "every index appears once". That class can also have methods to do trimming (both on time and size).

Does it make sense?

@abeyad
Copy link
Author

abeyad commented Mar 23, 2016

@bleskes ++ on queue of deleted objects for ease of insertion and trimming from the front. The map made it easier to assert "every index appears once" semantics, but I can separate the internal representation from what is serialized.

Every time you add an entry the class automatically captures the current time (both in millis and in nanos) and add it to an internal key class.

I'm not clear on this - I figured we would need the current time on each entry (hence creating the IndexTombstone class to represent each entry). I'm not sure exactly what you mean by the adding current time to an internal key class.

private static final String INDEX_NAME_KEY = "indexName";
private static final String DELETE_DATE_KEY = "deleteDate";
private static final String CLUSTER_VERSION_KEY = "clusterVersion";
private static final ObjectParser<IndexTombstone.Builder, Void> TOMBSTONE_PARSER = new ObjectParser<>("indexTombstone");
Copy link
Contributor

Choose a reason for hiding this comment

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

YAY

@s1monw
Copy link
Contributor

s1monw commented Mar 23, 2016

Hi @abeyad . Thanks for picking it up. I think this can be done in a single tombstone class which is basically a queue of deleted Index object. new entries are always added at the end. Trimming is always done at the beginning. Every time you add an entry the class automatically captures the current time (both in millis and in nanos) and add it to an internal key class. Internally we can assert semantics like "every index appears once". That class can also have methods to do trimming (both on time and size).

I think the current design is OK. It's really a value object and doesn't contain logic. It has the serializaiton and deserialization in there which is good. It can also implement comparable which is then taking the time into account. I also think we shouldn't mix datastructure that is on the clusterstate and representation.

Regarding a queue, I think we should just stick with a simple list we can sort once it's modified and ensure in the Clusterstate ctor that is in-fact sorted but keep it simple.

I also think we might even go without pruning in thirst PR and do the pruning as a followup? It can block a lot of good progress. There are a lot of open questions related to this and for how long we keep there tombstones, I think we should try to keep them for as long as possible but the question of how long is very hard to answer.

@abeyad abeyad force-pushed the feature/tombstone-deleted-indices branch 7 times, most recently from 8d610f6 to e563dce Compare March 28, 2016 16:02
@abeyad abeyad changed the title WIP: Adds tombstones to cluster state for index deletions Adds tombstones to cluster state for index deletions Mar 28, 2016
@abeyad abeyad force-pushed the feature/tombstone-deleted-indices branch from e563dce to a38e12e Compare March 28, 2016 16:11
indicesService.deleteClosedIndex("closed index no longer part of the metadata", metaData, event.state());
} else {
indexSettings = null;
}
Copy link
Author

Choose a reason for hiding this comment

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

@bleskes I had to change the logic here, because in the case of a node restarting, its previous state will not contain the index metadata for an index that was deleted while it was offline, so if the index metadata for the deleted index (part of the tombstones in the cluster state) is not in the previous cluster state, I try to read it off disk. I had to introduce the MetaStateService as a dependency in this class in order to do that. If the index metadata could not also be read off of disk, that means the index was both created and deleted while the node was offline, so there is nothing to do.

I am unsure if this is the best approach, so would appreciate your feedback.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I see what you mean. If the node is restarted while the index was deleted it may have no previous state. I think what you do is the right thing, but we can structure it in a slightly cleaner way:

if (idxService != null) {
  // delete in memory index
                  deleteIndex(index, "index no longer part of the metadata");

    // ackNodeIndexDeleted
} else if (previousState.metaData().hasIndex(index)) { < --- needs a variant the checks a uuid
  // deleted index which wasn't assigned to local node (the closed index is very misleading below) 
  indicesService.deleteClosedIndex("closed index no longer part of the metadata", metaData, event.state());
  // ack the index deletion 
} else if (indicesService.canDeleteIndex(Index)) { <-- which should also checks for the folder existence like   
  // load metadata from file and delete it
}

wdyt?

Copy link
Author

Choose a reason for hiding this comment

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

That makes sense, I will formulate the logic in that manner.

Copy link
Author

Choose a reason for hiding this comment

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

Only issue with

else if (indicesService.canDeleteIndex(Index))

is that canDeleteIndexContents requires the IndexSettings, which can't be created until the IndexMetaData is loaded, so all that logic will need to go in an else block..

@abeyad
Copy link
Author

abeyad commented Mar 28, 2016

@bleskes @s1monw This PR is ready for the next round of review. I left two specific comments that need special attention, please, as I was unsure of the proper route to take:
https://github.com/elastic/elasticsearch/pull/17265/files#r57590545
https://github.com/elastic/elasticsearch/pull/17265/files#r57592054

@abeyad abeyad force-pushed the feature/tombstone-deleted-indices branch from a38e12e to 0eacfbf Compare March 29, 2016 20:45
private final List<Tombstone> tombstones;

private IndexGraveyard(final List<Tombstone> list) {
tombstones = Collections.unmodifiableList(list);
Copy link
Member

Choose a reason for hiding this comment

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

I think that we want a null check here?

Copy link
Author

Choose a reason for hiding this comment

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

Its a private constructor, only called from the Builder, would an assert be more appropriate?

Copy link
Member

@jasontedor jasontedor Apr 25, 2016

Choose a reason for hiding this comment

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

Its a private constructor, only called from the Builder, would an assert be more appropriate?

An assert is fine.

@jasontedor
Copy link
Member

@abeyad I think that the high-level concepts have been ironed out, but I left some feedback on coding details.

@abeyad abeyad force-pushed the feature/tombstone-deleted-indices branch from 52d8adc to c71e1b6 Compare April 25, 2016 17:01
@abeyad abeyad force-pushed the feature/tombstone-deleted-indices branch from c71e1b6 to 751f5a8 Compare April 25, 2016 17:03
@@ -175,6 +177,7 @@ public IndexGraveyard readFrom(final StreamInput in) throws IOException {
final public static class Builder {
private List<Tombstone> tombstones;
private int numPurged = -1;
private long currentTime = System.currentTimeMillis();
Copy link
Member

Choose a reason for hiding this comment

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

Can it be final?

@jasontedor
Copy link
Member

LGTM. Great work @abeyad.

@abeyad
Copy link
Author

abeyad commented Apr 25, 2016

@jasontedor thank you for all the valuable feedback!

@abeyad abeyad closed this in d39eb2d Apr 25, 2016
@clintongormley
Copy link
Contributor

@abeyad looks like these settings still need to be documented?

@clintongormley clintongormley added :Distributed Indexing/Distributed A catch all label for anything in the Distributed Area. Please avoid if you can. and removed :Cluster labels Feb 13, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
:Distributed Indexing/Distributed A catch all label for anything in the Distributed Area. Please avoid if you can. >enhancement v5.0.0-alpha2
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants