-
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
Adds tombstones to cluster state for index deletions #17265
Conversation
494bf97
to
44d8f05
Compare
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) { |
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.
I think we should compare the previous tombstone and the current one and generate a delta. Don't try to be overly smart.
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? |
@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.
I'm not clear on this - I figured we would need the current time on each entry (hence creating the |
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"); |
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.
YAY
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. |
8d610f6
to
e563dce
Compare
e563dce
to
a38e12e
Compare
indicesService.deleteClosedIndex("closed index no longer part of the metadata", metaData, event.state()); | ||
} else { | ||
indexSettings = null; | ||
} |
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.
@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.
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.
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?
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.
That makes sense, I will formulate the logic in that manner.
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.
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..
@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: |
a38e12e
to
0eacfbf
Compare
private final List<Tombstone> tombstones; | ||
|
||
private IndexGraveyard(final List<Tombstone> list) { | ||
tombstones = Collections.unmodifiableList(list); |
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.
I think that we want a null check here?
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.
Its a private constructor, only called from the Builder, would an assert be more appropriate?
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.
Its a private constructor, only called from the Builder, would an assert be more appropriate?
An assert is fine.
@abeyad I think that the high-level concepts have been ironed out, but I left some feedback on coding details. |
52d8adc
to
c71e1b6
Compare
c71e1b6
to
751f5a8
Compare
@@ -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(); |
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.
Can it be final?
LGTM. Great work @abeyad. |
@jasontedor thank you for all the valuable feedback! |
@abeyad looks like these settings still need to be documented? |
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