package proj.zoie.impl.indexing.internal; /** * 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. */ import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongSet; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import org.apache.lucene.index.IndexReader; import proj.zoie.api.ZoieIndexReader; import proj.zoie.api.impl.util.FileUtil; import proj.zoie.api.impl.util.LongSetAccelerator; import proj.zoie.api.indexing.IndexReaderDecorator; import proj.zoie.impl.indexing.internal.IndexReaderDispenser.InternalIndexReader; public class SearchIndexManager<R extends IndexReader>{ private static final Logger log = Logger.getLogger(SearchIndexManager.class); public static enum Status { Sleep, Working } private File _location; private final IndexReaderDecorator<R> _indexReaderDecorator; private volatile DiskSearchIndex<R> _diskIndex; private volatile Status _diskIndexerStatus; private volatile Mem<R> _mem; /** * @param location * @param indexReaderDecorator */ public SearchIndexManager(File location,IndexReaderDecorator<R> indexReaderDecorator) { _location = location; if (indexReaderDecorator!=null) { _indexReaderDecorator=indexReaderDecorator; } else { throw new IllegalArgumentException("indexReaderDecorator cannot be null"); } init(); } public File getDiskIndexLocation() { return _location; } public int getDiskSegmentCount() throws IOException{ return _diskIndex.getSegmentCount(); } public void setNumLargeSegments(int numLargeSegments) { _diskIndex._mergePolicyParams.setNumLargeSegments(numLargeSegments); } public int getNumLargeSegments() { return _diskIndex._mergePolicyParams.getNumLargeSegments(); } public void setMaxSmallSegments(int maxSmallSegments) { _diskIndex._mergePolicyParams.setMaxSmallSegments(maxSmallSegments); } public int getMaxSmallSegments() { return _diskIndex._mergePolicyParams.getMaxSmallSegments(); } public void setPartialExpunge(boolean doPartialExpunge) { _diskIndex._mergePolicyParams.setPartialExpunge(doPartialExpunge); } public boolean getPartialExpunge() { return _diskIndex._mergePolicyParams.getPartialExpunge(); } public void setMergeFactor(int mergeFactor) { _diskIndex._mergePolicyParams.setMergeFactor(mergeFactor); } public int getMergeFactor() { return _diskIndex._mergePolicyParams.getMergeFactor(); } public void setMaxMergeDocs(int maxMergeDocs) { _diskIndex._mergePolicyParams.setMaxMergeDocs(maxMergeDocs); } public int getMaxMergeDocs() { return _diskIndex._mergePolicyParams.getMaxMergeDocs(); } public void setUseCompoundFile(boolean useCompoundFile) { _diskIndex._mergePolicyParams.setUseCompoundFile(useCompoundFile); } public boolean isUseCompoundFile() { return _diskIndex._mergePolicyParams.isUseCompoundFile(); } /** * Gets the current disk indexer status * @return */ public Status getDiskIndexerStatus() { return _diskIndexerStatus; } public void returnReaders(List<ZoieIndexReader<R>> readers){ _diskIndex.returnReaders(readers); } public List<ZoieIndexReader<R>> getIndexReaders() throws IOException { ArrayList<ZoieIndexReader<R>> readers = new ArrayList<ZoieIndexReader<R>>(); ZoieIndexReader<R> reader = null; Mem<R> mem = _mem; RAMSearchIndex<R> memIndexB = mem.get_memIndexB(); RAMSearchIndex<R> memIndexA = mem.get_memIndexA(); synchronized(this) { if (memIndexB != null) // load memory index B { reader = memIndexB.openIndexReader(); if (reader != null) { reader.setDelDocIds(); readers.add(reader); } } if (memIndexA != null) // load memory index A { reader = memIndexA.openIndexReader(); if (reader != null) { reader.setDelDocIds(); readers.add(reader); } } if (_diskIndex != null) // load disk index { reader = mem.get_diskIndexReader(); if (reader != null) { reader.setDelDocIds(); readers.add(reader); } } } return readers; } public void setDiskIndexerStatus(Status status) { // going from sleep to wake, disk index starts to index // which according to the spec, index B is created and it starts to collect data // IMPORTANT: do nothing if the status is not being changed. if (_diskIndexerStatus != status) { log.info("updating batch indexer status from "+_diskIndexerStatus+" to "+status); if (status == Status.Working) { // sleeping to working long version = _diskIndex.getVersion(); Mem<R> oldMem = _mem; RAMSearchIndex<R> memIndexA = oldMem.get_memIndexA(); if(memIndexA != null) memIndexA.closeIndexWriter(); RAMSearchIndex<R> memIndexB = new RAMSearchIndex<R>(version, _indexReaderDecorator); Mem<R> mem = new Mem<R>(memIndexA, memIndexB, memIndexB, memIndexA, oldMem.get_diskIndexReader()); _mem = mem; log.info("Current writable index is B, new B created"); } else { // from working to sleep ZoieIndexReader<R> diskIndexReader = null; try { // a new reader is already loaded in loadFromIndex diskIndexReader = _diskIndex.openIndexReader(); } catch (IOException e) { log.error(e.getMessage(),e); return; } Mem<R> oldMem = _mem; Mem<R> mem = new Mem<R>(oldMem.get_memIndexB(), null, oldMem.get_memIndexB(), null, diskIndexReader); _mem = mem; log.info("Current writable index is A, B is flushed"); } _diskIndexerStatus = status; } } /** * Initialization */ private void init() { _diskIndexerStatus = Status.Sleep; _diskIndex = new DiskSearchIndex<R>(_location, _indexReaderDecorator); ZoieIndexReader<R> diskIndexReader = null; if(_diskIndex != null) { try { diskIndexReader = _diskIndex.getNewReader(); } catch (IOException e) { log.error(e.getMessage(),e); return; } } long version = _diskIndex.getVersion(); RAMSearchIndex<R> memIndexA = new RAMSearchIndex<R>(version, _indexReaderDecorator); Mem<R> mem = new Mem<R>(memIndexA, null, memIndexA, null, diskIndexReader); _mem = mem; } public DiskSearchIndex<R> getDiskIndex() { return _diskIndex; } public RAMSearchIndex<R> getCurrentWritableMemoryIndex() { return _mem.get_currentWritable(); } public RAMSearchIndex<R> getCurrentReadOnlyMemoryIndex() { return _mem.get_currentReadOnly(); } /** * Clean up */ public void close(){ if (_diskIndex!=null) { _diskIndex.close(); } Mem<R> mem = _mem; if (mem.get_memIndexA()!=null) { mem.get_memIndexA().close(); } if (mem.get_memIndexB()!=null) { mem.get_memIndexB().close(); } } public long getCurrentDiskVersion() throws IOException { return (_diskIndex==null) ? 0 : _diskIndex.getVersion(); } public int getDiskIndexSize() { return (_diskIndex==null) ? 0 : _diskIndex.getNumdocs(); } public int getRamAIndexSize() { RAMSearchIndex<R> memIndexA = _mem.get_memIndexA(); return (memIndexA==null) ? 0 : memIndexA.getNumdocs(); } public long getRamAVersion() { RAMSearchIndex<R> memIndexA = _mem.get_memIndexA(); return (memIndexA==null) ? 0L : memIndexA.getVersion(); } public int getRamBIndexSize() { RAMSearchIndex<R> memIndexB = _mem.get_memIndexB(); return (memIndexB==null) ? 0 : memIndexB.getNumdocs(); } public long getRamBVersion() { RAMSearchIndex<R> memIndexB = _mem.get_memIndexB(); return (memIndexB==null) ? 0L : memIndexB.getVersion(); } /** * utility method to delete a directory * @param dir * @throws IOException */ private static void deleteDir(File dir) throws IOException { if (dir == null) return; if (dir.isDirectory()) { File[] files=dir.listFiles(); for (File file : files) { deleteDir(file); } if (!dir.delete()) { throw new IOException("cannot remove directory: "+dir.getAbsolutePath()); } } else { if (!dir.delete()) { throw new IOException("cannot delete file: "+dir.getAbsolutePath()); } } } /** * Purges an index */ public void purgeIndex() { log.info("purging index ..."); FileUtil.rmDir(_location); if(_diskIndex != null) { _diskIndex.clearDeletes(); _diskIndex.refresh(); RAMSearchIndex<R> memIndexA = new RAMSearchIndex<R>(_diskIndex.getVersion(), _indexReaderDecorator); Mem<R> mem = new Mem<R>(memIndexA, null, memIndexA, null, null); _mem = mem; } log.info("index purged"); } public void refreshDiskReader() throws IOException { log.info("refreshing disk reader ..."); ZoieIndexReader<R> diskIndexReader = null; try { // load a new reader, not in the lock because this should be done in the background // and should not contend with the readers diskIndexReader = _diskIndex.getNewReader(); } catch(IOException e) { log.error(e.getMessage(),e); if(diskIndexReader != null) diskIndexReader.close(); throw e; } Mem<R> oldMem = _mem; Mem<R> mem = new Mem<R>(oldMem.get_memIndexA(), oldMem.get_memIndexB(), oldMem.get_currentWritable(), oldMem.get_currentReadOnly(), diskIndexReader); _mem = mem; log.info("disk reader refreshed"); } private final static class Mem<R extends IndexReader> { private final RAMSearchIndex<R> _memIndexA; private final RAMSearchIndex<R> _memIndexB; private final RAMSearchIndex<R> _currentWritable; private final RAMSearchIndex<R> _currentReadOnly; private final ZoieIndexReader<R> _diskIndexReader; Mem(RAMSearchIndex<R> a, RAMSearchIndex<R> b, RAMSearchIndex<R> w, RAMSearchIndex<R> r, ZoieIndexReader<R> d) { _memIndexA = a; _memIndexB = b; _currentWritable = w; _currentReadOnly = r; _diskIndexReader = d; } protected RAMSearchIndex<R> get_memIndexA() { return _memIndexA; } protected RAMSearchIndex<R> get_memIndexB() { return _memIndexB; } protected RAMSearchIndex<R> get_currentWritable() { return _currentWritable; } protected RAMSearchIndex<R> get_currentReadOnly() { return _currentReadOnly; } protected ZoieIndexReader<R> get_diskIndexReader() { return _diskIndexReader; } } }