All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.lucene.index.SnapshotDeletionPolicy Maven / Gradle / Ivy

There is a newer version: 6.4.2_1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.lucene.index;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.lucene.store.Directory;

/**
 * An {@link IndexDeletionPolicy} that wraps any other {@link IndexDeletionPolicy} and adds the
 * ability to hold and later release snapshots of an index. While a snapshot is held, the {@link
 * IndexWriter} will not remove any files associated with it even if the index is otherwise being
 * actively, arbitrarily changed. Because we wrap another arbitrary {@link IndexDeletionPolicy},
 * this gives you the freedom to continue using whatever {@link IndexDeletionPolicy} you would
 * normally want to use with your index.
 *
 * 

This class maintains all snapshots in-memory, and so the information is not persisted and not * protected against system failures. If persistence is important, you can use {@link * PersistentSnapshotDeletionPolicy}. * * @lucene.experimental */ public class SnapshotDeletionPolicy extends IndexDeletionPolicy { /** Records how many snapshots are held against each commit generation */ protected final Map refCounts = new HashMap<>(); /** Used to map gen to IndexCommit. */ protected final Map indexCommits = new HashMap<>(); /** Wrapped {@link IndexDeletionPolicy} */ private final IndexDeletionPolicy primary; /** Most recently committed {@link IndexCommit}. */ protected IndexCommit lastCommit; /** Used to detect misuse */ private boolean initCalled; /** Sole constructor, taking the incoming {@link IndexDeletionPolicy} to wrap. */ public SnapshotDeletionPolicy(IndexDeletionPolicy primary) { this.primary = primary; } @Override public synchronized void onCommit(List commits) throws IOException { primary.onCommit(wrapCommits(commits)); lastCommit = commits.get(commits.size() - 1); } @Override public synchronized void onInit(List commits) throws IOException { initCalled = true; primary.onInit(wrapCommits(commits)); for (IndexCommit commit : commits) { if (refCounts.containsKey(commit.getGeneration())) { indexCommits.put(commit.getGeneration(), commit); } } if (!commits.isEmpty()) { lastCommit = commits.get(commits.size() - 1); } } /** * Release a snapshotted commit. * * @param commit the commit previously returned by {@link #snapshot} */ public synchronized void release(IndexCommit commit) throws IOException { long gen = commit.getGeneration(); releaseGen(gen); } /** Release a snapshot by generation. */ protected void releaseGen(long gen) throws IOException { if (!initCalled) { throw new IllegalStateException( "this instance is not being used by IndexWriter; be sure to use the instance returned from writer.getConfig().getIndexDeletionPolicy()"); } Integer refCount = refCounts.get(gen); if (refCount == null) { throw new IllegalArgumentException("commit gen=" + gen + " is not currently snapshotted"); } int refCountInt = refCount.intValue(); assert refCountInt > 0; refCountInt--; if (refCountInt == 0) { refCounts.remove(gen); indexCommits.remove(gen); } else { refCounts.put(gen, refCountInt); } } /** Increments the refCount for this {@link IndexCommit}. */ protected synchronized void incRef(IndexCommit ic) { long gen = ic.getGeneration(); Integer refCount = refCounts.get(gen); int refCountInt; if (refCount == null) { indexCommits.put(gen, lastCommit); refCountInt = 0; } else { refCountInt = refCount.intValue(); } refCounts.put(gen, refCountInt + 1); } /** * Snapshots the last commit and returns it. Once a commit is 'snapshotted,' it is protected from * deletion (as long as this {@link IndexDeletionPolicy} is used). The snapshot can be removed by * calling {@link #release(IndexCommit)} followed by a call to {@link * IndexWriter#deleteUnusedFiles()}. * *

NOTE: while the snapshot is held, the files it references will not be deleted, which * will consume additional disk space in your index. If you take a snapshot at a particularly bad * time (say just before you call forceMerge) then in the worst case this could consume an extra * 1X of your total index size, until you release the snapshot. * * @throws IllegalStateException if this index does not have any commits yet * @return the {@link IndexCommit} that was snapshotted. */ public synchronized IndexCommit snapshot() throws IOException { if (!initCalled) { throw new IllegalStateException( "this instance is not being used by IndexWriter; be sure to use the instance returned from writer.getConfig().getIndexDeletionPolicy()"); } if (lastCommit == null) { // No commit yet, eg this is a new IndexWriter: throw new IllegalStateException("No index commit to snapshot"); } incRef(lastCommit); return lastCommit; } /** Returns all IndexCommits held by at least one snapshot. */ public synchronized List getSnapshots() { return new ArrayList<>(indexCommits.values()); } /** Returns the total number of snapshots currently held. */ public synchronized int getSnapshotCount() { int total = 0; for (Integer refCount : refCounts.values()) { total += refCount.intValue(); } return total; } /** * Retrieve an {@link IndexCommit} from its generation; returns null if this IndexCommit is not * currently snapshotted */ public synchronized IndexCommit getIndexCommit(long gen) { return indexCommits.get(gen); } /** Wraps each {@link IndexCommit} as a {@link SnapshotCommitPoint}. */ private List wrapCommits(List commits) { List wrappedCommits = new ArrayList<>(commits.size()); for (IndexCommit ic : commits) { wrappedCommits.add(new SnapshotCommitPoint(ic)); } return wrappedCommits; } /** Wraps a provided {@link IndexCommit} and prevents it from being deleted. */ private class SnapshotCommitPoint extends IndexCommit { /** The {@link IndexCommit} we are preventing from deletion. */ protected IndexCommit cp; /** Creates a {@code SnapshotCommitPoint} wrapping the provided {@link IndexCommit}. */ protected SnapshotCommitPoint(IndexCommit cp) { this.cp = cp; } @Override public String toString() { return "SnapshotDeletionPolicy.SnapshotCommitPoint(" + cp + ")"; } @Override public void delete() { synchronized (SnapshotDeletionPolicy.this) { // Suppress the delete request if this commit point is // currently snapshotted. if (!refCounts.containsKey(cp.getGeneration())) { cp.delete(); } } } @Override public Directory getDirectory() { return cp.getDirectory(); } @Override public Collection getFileNames() throws IOException { return cp.getFileNames(); } @Override public long getGeneration() { return cp.getGeneration(); } @Override public String getSegmentsFileName() { return cp.getSegmentsFileName(); } @Override public Map getUserData() throws IOException { return cp.getUserData(); } @Override public boolean isDeleted() { return cp.isDeleted(); } @Override public int getSegmentCount() { return cp.getSegmentCount(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy