Skip to content

Commit

Permalink
Prevent cache file synchronization to add evicted cache files back in…
Browse files Browse the repository at this point in the history
… the persistent cache (#68042)

This commit changes how cache files synchronization interacts with
the persistent cache in searchable snapshots. Before this change it
was possible that synchronization reintroduces information about
an evicted cache file in the persistent cache Lucene index.

This commit introduces an queue of cache file events that are
periodically processed by the cache synchronization method. The
events refer to a specific cache file and a type of event (deletion or
fsync needed) that must be processed by the cache synchronization
method, which in turn applies the appropriate logic to the persistent
cache Lucene. This commit changes how the event are inserted into
the events queue by guaranteeing that no need fsync/update event
come after a delete event.
index.

Backport of #67694
  • Loading branch information
tlrx committed Jan 27, 2021
1 parent 7424d1d commit 4840139
Show file tree
Hide file tree
Showing 6 changed files with 569 additions and 142 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ public interface EvictionListener {
void onEviction(CacheFile evictedCacheFile);
}

/**
* {@link ModificationListener} can be used to be notified when a {@link CacheFile} needs to be fsynced or is deleted.
*/
public interface ModificationListener {
void onCacheFileNeedsFsync(CacheFile cacheFile);

void onCacheFileDelete(CacheFile cacheFile);
}

private static final StandardOpenOption[] OPEN_OPTIONS = new StandardOpenOption[] {
StandardOpenOption.READ,
StandardOpenOption.WRITE,
Expand All @@ -60,6 +69,8 @@ protected void closeInternal() {
Files.deleteIfExists(file);
} catch (IOException e) {
throw new UncheckedIOException(e);
} finally {
listener.onCacheFileDelete(CacheFile.this);
}
}
};
Expand All @@ -78,10 +89,9 @@ protected void closeInternal() {
private final AtomicBoolean needsFsync = new AtomicBoolean();

/**
* A runnable that is executed every time the {@link #needsFsync} flag is toggled to {@code true}, which indicates that the cache file
* has been updated. See {@link #markAsNeedsFSync()} method.
* A {@link ModificationListener} that can be used to be notified when the cache file is updated or deleted.
*/
private final Runnable needsFsyncRunnable;
private final ModificationListener listener;

/**
* A reference counted holder for the current channel to the physical file backing this cache file instance.
Expand Down Expand Up @@ -123,19 +133,19 @@ protected void closeInternal() {
@Nullable
private volatile FileChannelReference channelRef;

public CacheFile(CacheKey cacheKey, long length, Path file, Runnable onNeedFSync) {
this(cacheKey, new SparseFileTracker(file.toString(), length), file, onNeedFSync);
public CacheFile(CacheKey cacheKey, long length, Path file, ModificationListener listener) {
this(cacheKey, new SparseFileTracker(file.toString(), length), file, listener);
}

public CacheFile(CacheKey cacheKey, long length, Path file, SortedSet<Tuple<Long, Long>> ranges, Runnable onNeedFSync) {
this(cacheKey, new SparseFileTracker(file.toString(), length, ranges), file, onNeedFSync);
public CacheFile(CacheKey cacheKey, long length, Path file, SortedSet<Tuple<Long, Long>> ranges, ModificationListener listener) {
this(cacheKey, new SparseFileTracker(file.toString(), length, ranges), file, listener);
}

private CacheFile(CacheKey cacheKey, SparseFileTracker tracker, Path file, Runnable onNeedFSync) {
private CacheFile(CacheKey cacheKey, SparseFileTracker tracker, Path file, ModificationListener listener) {
this.cacheKey = Objects.requireNonNull(cacheKey);
this.tracker = Objects.requireNonNull(tracker);
this.file = Objects.requireNonNull(file);
this.needsFsyncRunnable = Objects.requireNonNull(onNeedFSync);
this.listener = Objects.requireNonNull(listener);
assert invariant();
}

Expand Down Expand Up @@ -361,11 +371,11 @@ protected void doRun() throws Exception {
try {
ensureOpen();
writer.fillCacheRange(reference.fileChannel, gap.start(), gap.end(), gap::onProgress);
gap.onCompletion();
markAsNeedsFSync();
} finally {
reference.decRef();
}
gap.onCompletion();
markAsNeedsFSync();
}

@Override
Expand Down Expand Up @@ -469,8 +479,9 @@ boolean needsFsync() {
* Marks the current cache file as "fsync needed" and notifies the corresponding listener.
*/
private void markAsNeedsFSync() {
assert refCounter.refCount() > 0 : "file should not be fully released";
if (needsFsync.getAndSet(true) == false) {
needsFsyncRunnable.run();
listener.onCacheFileNeedsFsync(this);
}
}

Expand Down

0 comments on commit 4840139

Please sign in to comment.