package org.infinispan.lucene.impl; import java.io.IOException; import java.util.Collection; import java.util.concurrent.Executor; import org.apache.lucene.store.Directory; import org.apache.lucene.store.IOContext; import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexOutput; import org.apache.lucene.store.Lock; import org.apache.lucene.store.LockFactory; import org.infinispan.Cache; import org.infinispan.context.Flag; import org.infinispan.lucene.FileCacheKey; import org.infinispan.lucene.readlocks.SegmentReadLocker; import org.infinispan.util.logging.Log; import org.infinispan.util.logging.LogFactory; /** * Directory implementation for Apache Lucene. * Meant to be compatible with versions 5.0+ * * @since 5.2 * @author Sanne Grinovero * @see org.apache.lucene.store.Directory * @see org.apache.lucene.store.LockFactory */ class DirectoryLucene extends Directory implements DirectoryExtensions { private static final Log log = LogFactory.getLog(DirectoryLucene.class); private static final boolean trace = log.isTraceEnabled(); private final DirectoryImplementor impl; // indexName is used to be able to store multiple named indexes in the same caches private final String indexName; private final Executor deleteExecutor; private final int affinitySegmentId; private volatile LockFactory lockFactory; /** * @param metadataCache the cache to be used for all smaller metadata: prefer replication over distribution, avoid eviction * @param chunksCache the cache to use for the space consuming segments: prefer distribution, enable eviction if needed * @param distLocksCache the cache to use for locks, to avoid more than one process to write to the index * @param indexName the unique index name, useful to store multiple indexes in the same caches * @param lf the LockFactory to be used by IndexWriters. @see org.infinispan.lucene.locking * @param chunkSize segments are fragmented in chunkSize bytes; larger values are more efficient for searching but less for distribution and network replication * @param readLocker @see org.infinispan.lucene.readlocks for some implementations; you might be able to provide more efficient implementations by controlling the IndexReader's lifecycle. * @param fileListUpdatedAsync When true, the writes to the list of currently existing files in the Directory will use the putAsync method rather than put. * @param deleteExecutor The Executor to run file deletes in the background * @param affinitySegmentId A hint interpreted by the consistent hashing function to force locality with a specific segment identifier */ public DirectoryLucene(Cache<?, ?> metadataCache, Cache<?, ?> chunksCache, Cache<?, ?> distLocksCache, String indexName, LockFactory lf, int chunkSize, SegmentReadLocker readLocker, boolean fileListUpdatedAsync, Executor deleteExecutor, int affinitySegmentId) { this.deleteExecutor = deleteExecutor; this.affinitySegmentId = affinitySegmentId; this.impl = new DirectoryImplementor(metadataCache, chunksCache, distLocksCache, indexName, chunkSize, readLocker, fileListUpdatedAsync, affinitySegmentId); this.indexName = indexName; this.lockFactory = lf; } /** * {@inheritDoc} */ @Override public void deleteFile(final String name) { ensureOpen(); deleteExecutor.execute(new DeleteTask(name)); } /** * {@inheritDoc} */ @Override public void renameFile(final String from, final String to) { impl.renameFile(from, to); } /** * {@inheritDoc} */ @Override public long fileLength(final String name) { ensureOpen(); return impl.fileLength(name); } /** * {@inheritDoc} */ @Override public IndexOutput createOutput(final String name, final IOContext context) throws IOException { return impl.createOutput(name); } /** * {@inheritDoc} */ @Override public IndexInput openInput(final String name, final IOContext context) throws IOException { final IndexInputContext indexInputContext = impl.openInput(name); if (indexInputContext.readLocks == null) { return new SingleChunkIndexInput(indexInputContext); } else { return new InfinispanIndexInput(indexInputContext); } } /** * {@inheritDoc} */ @Override public void close() { // Note the we don't really keep track of this anymore } @Override public String toString() { return "InfinispanDirectory{indexName=\'" + indexName + "\'}"; } /** * {@inheritDoc} */ @Override public String[] listAll() { return impl.list(); } /** * @return The value of indexName, same constant as provided to the constructor. */ @Override public String getIndexName() { return indexName; } public int getAffinitySegmentId() { return affinitySegmentId; } /** * {@inheritDoc} */ @Override public void sync(Collection<String> names) throws IOException { //This implementation is always in sync with the storage, so NOOP is fine } @Override public Lock obtainLock(String lockName) throws IOException { return lockFactory.obtainLock(this, lockName); } @Override public int getChunkSize() { return impl.getChunkSize(); } @Override public Cache getMetadataCache() { return impl.getMetadataCache(); } @Override public Cache getDataCache() { return impl.getDataCache(); } /** * Force release of the lock in this directory. Make sure to understand the * consequences */ @Override public void forceUnlock(String lockName) { Cache<Object, Integer> lockCache = getDistLockCache().getAdvancedCache().withFlags(Flag.SKIP_CACHE_STORE, Flag.SKIP_CACHE_LOAD); FileCacheKey fileCacheKey = new FileCacheKey(indexName, lockName, affinitySegmentId); Object previousValue = lockCache.remove(fileCacheKey); if (previousValue!=null && trace) { log.tracef("Lock forcibly removed for index: %s", indexName); } } public Cache<Object, Integer> getDistLockCache() { return impl.getDistLocksCache(); } final class DeleteTask implements Runnable { private final String fileName; private DeleteTask(String fileName) { this.fileName = fileName; } public String getFileName() { return fileName; } @Override public void run() { impl.deleteFile(fileName); } } }