cn.hbu.cs.esearch.index.IndexReaderDispenser.java Source code

Java tutorial

Introduction

Here is the source code for cn.hbu.cs.esearch.index.IndexReaderDispenser.java

Source

/*
 * 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 cn.hbu.cs.esearch.index;

import java.io.IOException;

import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.store.Directory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.hbu.cs.esearch.core.EsearchMultiReader;
import cn.hbu.cs.esearch.document.DirectoryManager;
import cn.hbu.cs.esearch.document.DocIDMapper;

public class IndexReaderDispenser<R extends IndexReader> {
    public static final Logger LOGGER = LoggerFactory.getLogger(IndexReaderDispenser.class);

    private static final int INDEX_OPEN_NUM_RETRIES = 5;
    private volatile EsearchMultiReader<R> _currentReader;
    private volatile IndexSignature _currentSignature;
    private final IndexReaderDecorator<R> _decorator;
    private final DirectoryManager _dirMgr;
    private final DiskSearchIndex<R> _idx;

    public IndexReaderDispenser(DirectoryManager dirMgr, IndexReaderDecorator<R> decorator,
            DiskSearchIndex<R> idx) {
        _idx = idx;
        _dirMgr = dirMgr;
        _decorator = decorator;
        _currentSignature = null;
        try {
            IndexSignature sig = new IndexSignature(_dirMgr.getVersion());
            if (sig != null) {
                getNewReader();
            }
        } catch (IOException e) {
            LOGGER.error("{}", e);
        }
    }

    public String getCurrentVersion() {
        return _currentSignature != null ? _currentSignature.getVersion() : null;
    }

    /**
     * constructs a new IndexReader instance
     * <p/>
     * Where the index is.
     *
     * @return Constructed IndexReader instance.
     *
     * @throws java.io.IOException
     */
    private EsearchMultiReader<R> newReader(DirectoryManager dirMgr, IndexReaderDecorator<R> decorator,
            IndexSignature signature) throws IOException {
        if (!dirMgr.exists()) {
            return null;
        }

        Directory dir = dirMgr.getDirectory();

        if (!DirectoryReader.indexExists(dir)) {
            return null;
        }

        int numTries = INDEX_OPEN_NUM_RETRIES;
        EsearchMultiReader<R> reader = null;

        // try max of 5 times, there might be a case where the segment file is being updated
        while (reader == null) {
            if (numTries == 0) {
                LOGGER.error("Problem refreshing disk index, all attempts failed.");
                throw new IOException("problem opening new index");
            }
            numTries--;

            try {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("opening index reader at: " + dirMgr.getPath());
                }
                DirectoryReader srcReader = DirectoryReader.open(dir);

                try {
                    reader = new EsearchMultiReader<R>(srcReader, decorator);
                    _currentSignature = signature;
                } catch (IOException ioe) {
                    // close the source reader if EsearchMultiReader construction fails
                    if (srcReader != null) {
                        srcReader.close();
                    }
                    throw ioe;
                }
            } catch (IOException ioe) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    LOGGER.warn("thread interrupted.");
                    continue;
                }
            }
        }
        return reader;
    }

    /**
     * get a fresh new reader instance
     *
     * @return an IndexReader instance, can be null if index does not yet exit
     *
     * @throws java.io.IOException
     */
    public EsearchMultiReader<R> getNewReader() throws IOException {
        int numTries = INDEX_OPEN_NUM_RETRIES;
        EsearchMultiReader<R> reader = null;

        // try it for a few times, there is a case where lucene is swapping the segment file,
        // or a case where the index directory file is updated, both are legitimate,
        // trying again does not block searchers,
        // the extra time it takes to get the reader, and to sync the index, memory index is collecting
        // docs

        while (reader == null) {
            if (numTries == 0) {
                break;
            }
            numTries--;
            try {
                IndexSignature sig = new IndexSignature(_dirMgr.getVersion());

                if (_currentReader == null) {
                    reader = newReader(_dirMgr, _decorator, sig);
                    break;
                } else {
                    reader = _currentReader.reopen();
                    _currentSignature = sig;
                }
            } catch (IOException ioe) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    LOGGER.warn("thread interrupted.");
                    continue;
                }
            }
        }

        // swap the internal readers
        if (_currentReader != reader) {
            if (reader != null) {
                DocIDMapper mapper = _idx._idxMgr._docIDMapperFactory.getDocIDMapper(reader);
                reader.setDocIDMapper(mapper);
            }
            // assume that this is the only place that _currentReader gets refreshed
            EsearchMultiReader<R> oldReader = _currentReader;
            _currentReader = reader;
            // we release our hold on the old reader so that it will be closed when
            // all the clients release their hold on it, the reader will be closed
            // automatically.
            LOGGER.info("swap disk reader and release old one from system");
            if (oldReader != null) {
                oldReader.decEsearchRef();
            }
        }
        return reader;
    }

    public EsearchMultiReader<R> getIndexReader() {
        if (_currentReader != null) {
            return _currentReader;
        } else {
            return null;
        }
    }

    /**
     * Closes the factory.
     */
    public void close() {
        closeReader();
    }

    /**
     * Closes the index reader
     */
    public void closeReader() {
        if (_currentReader != null) {
            _currentReader.decEsearchRef();
            int count = _currentReader.getInnerRefCount();
            LOGGER.info("final closeReader in dispenser and current refCount: " + count);
            if (count > 0) {
                LOGGER.warn(
                        "final closeReader call with reference count == " + count + " greater than 0. Potentially, "
                                + "the IndexReaders are not properly return to EsearchSystem.");
            }
            _currentReader = null;
        }
    }
}