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

Introduce a mechanism to notify plugin before an index/shard folder is going to be deleted from disk #65926

Merged
merged 13 commits into from
Dec 10, 2020

Conversation

tlrx
Copy link
Member

@tlrx tlrx commented Dec 7, 2020

This pull request introduces a new type of listeners IndexStorePlugin.IndexFoldersDeletionListener that allows plugins to be notified when an index folder (or a shard folder) is about to be deleted from disk.

This is useful for some plugins that require to take an action before folders are deleted, like searchable snapshots which should evict the cache files that are contained in the folders.

@tlrx tlrx force-pushed the index-folders-deletion-listeners branch from a670145 to fe0fbbd Compare December 7, 2020 16:17
@tlrx tlrx marked this pull request as ready for review December 8, 2020 08:18
@tlrx tlrx added the :Distributed Indexing/Distributed A catch all label for anything in the Distributed Area. Please avoid if you can. label Dec 8, 2020
@elasticmachine elasticmachine added the Team:Distributed (Obsolete) Meta label for distributed team (obsolete). Replaced by Distributed Indexing/Coordination. label Dec 8, 2020
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-distributed (Team:Distributed)

@tlrx tlrx added >enhancement v7.11.0 v8.0.0 and removed Team:Distributed (Obsolete) Meta label for distributed team (obsolete). Replaced by Distributed Indexing/Coordination. labels Dec 8, 2020
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.

We also apparently delete a shard folder in ShardPath#deleteLeftoverShardDirectory. It looks like that's mostly there for legacy reasons but I think we can also hit this if there are multiple data paths involved and we end up with the same shard on more than one path (requires changing the paths and restarting a few times).


final AtomicBoolean listener = new AtomicBoolean();
final LockObtainFailedException exception = expectThrows(LockObtainFailedException.class, () ->
env.deleteShardDirectoryUnderLock(sLock, indexSettings, indexPaths -> listener.set(true)));
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest assert false : indexPaths rather than listener.set(true), it might be useful to see the stack trace that led to calling this listener unexpectedly.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good suggestion, I pushed bd7aca5

* @param indexSettings settings for the index whose folders are going to be deleted
* @param indexPaths the paths of the folders that are going to be deleted
*/
default void beforeIndexFoldersDeleted(Index index, IndexSettings indexSettings, List<Path> indexPaths) {
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 don't need to supply a no-op default here, we can reasonably require implementations to implement both methods.

Copy link
Member Author

Choose a reason for hiding this comment

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

I pushed 5925e5a

public void deleteShardDirectorySafe(
ShardId shardId,
IndexSettings indexSettings,
Consumer<List<Path>> listener
Copy link
Contributor

Choose a reason for hiding this comment

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

All callers call List#of(Path[]) on the listener, maybe we should make this a Consumer<Path[]> instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok, I pushed 850d1a3 to use Path[] all over the place

@tlrx
Copy link
Member Author

tlrx commented Dec 9, 2020

We also apparently delete a shard folder in ShardPath#deleteLeftoverShardDirectory. It looks like that's mostly there for legacy reasons but I think we can also hit this if there are multiple data paths involved and we end up with the same shard on more than one path (requires changing the paths and restarting a few times).

Thanks for catching this place, I have no explanation why I missed it. I updated the pull request to also call listeners in deleteLeftoverShardDirectory but it took me quite some time to be able to craft a test that use it (I had to move around shard files on disk as there are verifications on on node ids and index uuids before this code is executed).

This is ready for another review.

@tlrx tlrx requested a review from DaveCTurner December 9, 2020 12:32
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, two tiny optional nits.

});
}

public void testListenersInvokedWhenIndexHasLeftOverShard() throws Exception {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice work, this looked to be pretty tricky to trigger 👍

SetOnce<Path[]> listener = new SetOnce<>();
ShardLockObtainFailedException ex = expectThrows(ShardLockObtainFailedException.class,
() -> env.deleteShardDirectorySafe(new ShardId(index, 0), idxSettings, listener::set));
assertNull(listener.get());
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe assert false rather than listener::set?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oups, I missed it the last time. I pushed aea4e6d

SetOnce<Path[]> listener = new SetOnce<>();
ShardLockObtainFailedException ex = expectThrows(ShardLockObtainFailedException.class,
() -> env.deleteIndexDirectorySafe(index, randomIntBetween(0, 10), idxSettings, listener::set));
assertNull(listener.get());
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe assert false rather than listener::set?

Copy link
Member Author

Choose a reason for hiding this comment

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

I pushed aea4e6d

@tlrx tlrx merged commit 34cdfee into elastic:master Dec 10, 2020
@tlrx
Copy link
Member Author

tlrx commented Dec 10, 2020

Thanks a lot David!

tlrx added a commit to tlrx/elasticsearch that referenced this pull request Dec 10, 2020
…s going to be deleted from disk (elastic#65926)

This commit introduces a new type of listeners
IndexStorePlugin.IndexFoldersDeletionListener
that allows plugins to be notified when an index
folder (or a shard folder) is about to be deleted
from disk.

This is useful for some plugins that require to
take an action before folders are deleted, like
searchable snapshots which should evict the
cache files that are contained in the folders.
tlrx added a commit that referenced this pull request Dec 10, 2020
…s going to be deleted from disk (#66158)

This commit introduces a new type of listeners
IndexStorePlugin.IndexFoldersDeletionListener
that allows plugins to be notified when an index
folder (or a shard folder) is about to be deleted
from disk.

This is useful for some plugins that require to
take an action before folders are deleted, like
searchable snapshots which should evict the
cache files that are contained in the folders.

Backport of #65926 for 7.x
tlrx added a commit that referenced this pull request Dec 11, 2020
…66173)

This commit changes the SearchableSnapshotDirectory so 
that it does not evict all its cache files at closing time, but 
instead delegates this work to the CacheService.

This change is motivated by the fact that Lucene directories 
are closed as the consequence of applying a new cluster 
state and as such the closing is executed within the cluster 
state applier thread; and we want to minimize disk IO 
operations in such thread (like deleting a lot of evicted 
cache files). It is also motivated by the future of the 
searchable snapshot cache which should become persistent.

This change is built on top of the existing 
SearchableSnapshotIndexEventListener and a new
 SearchableSnapshotIndexFoldersDeletionListener 
(see #65926) that are used to detect when a searchable 
snapshot index (or searchable snapshot shard) is 
removed from a data node.

When such a thing happens, the listeners notify the
 CacheService that maintains an internal list of removed 
shards. This list is used to evict the cache files associated 
to these shards as soon as possible (but not in the cluster 
state applier thread) or right before the same searchable 
snapshot shard is being built again on the same node.

In other situations like opening/closing a searchable 
snapshot shard then the cache files are not evicted 
anymore and should be reused.
tlrx added a commit to tlrx/elasticsearch that referenced this pull request Dec 14, 2020
…lastic#66173)

This commit changes the SearchableSnapshotDirectory so
that it does not evict all its cache files at closing time, but
instead delegates this work to the CacheService.

This change is motivated by the fact that Lucene directories
are closed as the consequence of applying a new cluster
state and as such the closing is executed within the cluster
state applier thread; and we want to minimize disk IO
operations in such thread (like deleting a lot of evicted
cache files). It is also motivated by the future of the
searchable snapshot cache which should become persistent.

This change is built on top of the existing
SearchableSnapshotIndexEventListener and a new
 SearchableSnapshotIndexFoldersDeletionListener
(see elastic#65926) that are used to detect when a searchable
snapshot index (or searchable snapshot shard) is
removed from a data node.

When such a thing happens, the listeners notify the
 CacheService that maintains an internal list of removed
shards. This list is used to evict the cache files associated
to these shards as soon as possible (but not in the cluster
state applier thread) or right before the same searchable
snapshot shard is being built again on the same node.

In other situations like opening/closing a searchable
snapshot shard then the cache files are not evicted
anymore and should be reused.
tlrx added a commit that referenced this pull request Dec 14, 2020
…66264)

This commit changes the SearchableSnapshotDirectory so
that it does not evict all its cache files at closing time, but
instead delegates this work to the CacheService.

This change is motivated by the fact that Lucene directories
are closed as the consequence of applying a new cluster
state and as such the closing is executed within the cluster
state applier thread; and we want to minimize disk IO
operations in such thread (like deleting a lot of evicted
cache files). It is also motivated by the future of the
searchable snapshot cache which should become persistent.

This change is built on top of the existing
SearchableSnapshotIndexEventListener and a new
 SearchableSnapshotIndexFoldersDeletionListener
(see #65926) that are used to detect when a searchable
snapshot index (or searchable snapshot shard) is
removed from a data node.

When such a thing happens, the listeners notify the
 CacheService that maintains an internal list of removed
shards. This list is used to evict the cache files associated
to these shards as soon as possible (but not in the cluster
state applier thread) or right before the same searchable
snapshot shard is being built again on the same node.

In other situations like opening/closing a searchable
snapshot shard then the cache files are not evicted
anymore and should be reused.

Backport of #66173 for 7.11
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 v7.11.0 v8.0.0-alpha1
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants