Package org.apache.blur.manager.writer

Source Code of org.apache.blur.manager.writer.BlurIndexSimpleWriter

/**
* 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.blur.manager.writer;

import static org.apache.blur.lucene.LuceneVersionConstant.LUCENE_VERSION;
import static org.apache.blur.utils.BlurConstants.BLUR_SHARD_QUEUE_MAX_INMEMORY_LENGTH;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

import org.apache.blur.BlurConfiguration;
import org.apache.blur.analysis.FieldManager;
import org.apache.blur.index.ExitableReader;
import org.apache.blur.index.IndexDeletionPolicyReader;
import org.apache.blur.log.Log;
import org.apache.blur.log.LogFactory;
import org.apache.blur.lucene.codec.Blur022Codec;
import org.apache.blur.lucene.warmup.TraceableDirectory;
import org.apache.blur.manager.indexserver.BlurIndexWarmup;
import org.apache.blur.server.IndexSearcherClosable;
import org.apache.blur.server.ShardContext;
import org.apache.blur.server.TableContext;
import org.apache.blur.thrift.generated.RowMutation;
import org.apache.blur.trace.Trace;
import org.apache.blur.trace.Tracer;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.BlurIndexWriter;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.TieredMergePolicy;
import org.apache.lucene.store.Directory;

public class BlurIndexSimpleWriter extends BlurIndex {

  private static final Log LOG = LogFactory.getLog(BlurIndexSimpleWriter.class);

  private final AtomicBoolean _isClosed = new AtomicBoolean();
  private final BlurIndexCloser _indexCloser;
  private final AtomicReference<DirectoryReader> _indexReader = new AtomicReference<DirectoryReader>();
  private final ExecutorService _searchThreadPool;
  private final Directory _directory;
  private final IndexWriterConfig _conf;
  private final TableContext _tableContext;
  private final FieldManager _fieldManager;
  private final ShardContext _shardContext;
  private final AtomicReference<BlurIndexWriter> _writer = new AtomicReference<BlurIndexWriter>();
  private final boolean _makeReaderExitable = true;
  private IndexImporter _indexImporter;
  private final ReentrantReadWriteLock _lock = new ReentrantReadWriteLock();
  private final WriteLock _writeLock = _lock.writeLock();
  private final ReadWriteLock _indexRefreshLock = new ReentrantReadWriteLock();
  private final Lock _indexRefreshWriteLock = _indexRefreshLock.writeLock();
  private final Lock _indexRefreshReadLock = _indexRefreshLock.readLock();
  private Thread _optimizeThread;
  private Thread _writerOpener;
  private final IndexDeletionPolicyReader _policy;
  private final SnapshotIndexDeletionPolicy _snapshotIndexDeletionPolicy;
  private final String _context;
  private final AtomicInteger _writesWaiting = new AtomicInteger();
  private final BlockingQueue<RowMutation> _queue;
  private final MutationQueueProcessor _mutationQueueProcessor;

  public BlurIndexSimpleWriter(ShardContext shardContext, Directory directory, SharedMergeScheduler mergeScheduler,
      final ExecutorService searchExecutor, BlurIndexCloser indexCloser, BlurIndexWarmup indexWarmup)
      throws IOException {
    super(shardContext, directory, mergeScheduler, searchExecutor, indexCloser, indexWarmup);
    _searchThreadPool = searchExecutor;
    _shardContext = shardContext;
    _tableContext = _shardContext.getTableContext();
    _context = _tableContext.getTable() + "/" + shardContext.getShard();
    _fieldManager = _tableContext.getFieldManager();
    Analyzer analyzer = _fieldManager.getAnalyzerForIndex();
    _conf = new IndexWriterConfig(LUCENE_VERSION, analyzer);
    _conf.setWriteLockTimeout(TimeUnit.MINUTES.toMillis(5));
    _conf.setCodec(new Blur022Codec(_tableContext.getBlurConfiguration()));
    _conf.setSimilarity(_tableContext.getSimilarity());
    _conf.setMergedSegmentWarmer(new BlurIndexReaderWarmer(shardContext, _isClosed, indexWarmup));
    TieredMergePolicy mergePolicy = (TieredMergePolicy) _conf.getMergePolicy();
    mergePolicy.setUseCompoundFile(false);
    _conf.setMergeScheduler(mergeScheduler.getMergeScheduler());
    _snapshotIndexDeletionPolicy = new SnapshotIndexDeletionPolicy(_tableContext.getConfiguration(), new Path(
        shardContext.getHdfsDirPath(), "generations"));
    _policy = new IndexDeletionPolicyReader(_snapshotIndexDeletionPolicy);
    _conf.setIndexDeletionPolicy(_policy);
    BlurConfiguration blurConfiguration = _tableContext.getBlurConfiguration();
    _queue = new ArrayBlockingQueue<RowMutation>(blurConfiguration.getInt(BLUR_SHARD_QUEUE_MAX_INMEMORY_LENGTH, 100));
    _mutationQueueProcessor = new MutationQueueProcessor(_queue, this, _shardContext, _writesWaiting);

    if (!DirectoryReader.indexExists(directory)) {
      new BlurIndexWriter(directory, _conf).close();
    }

    // This directory allows for warm up by adding tracing ability.
    TraceableDirectory dir = new TraceableDirectory(directory);
    _directory = dir;

    _indexCloser = indexCloser;
    _indexReader.set(wrap(DirectoryReader.open(_directory)));

    openWriter();
  }

  private synchronized void openWriter() {
    IOUtils.cleanup(LOG, _indexImporter);
    BlurIndexWriter writer = _writer.get();
    if (writer != null) {
      try {
        writer.close(false);
      } catch (IOException e) {
        LOG.error("Unknown error while trying to close the writer, [" + _shardContext.getTableContext().getTable()
            + "] Shard [" + _shardContext.getShard() + "]", e);
      }
      _writer.set(null);
    }
    _writerOpener = getWriterOpener(_shardContext);
    _writerOpener.start();
  }

  private DirectoryReader wrap(DirectoryReader reader) throws IOException {
    if (_makeReaderExitable) {
      reader = new ExitableReader(reader);
    }
    return _policy.register(reader);
  }

  private Thread getWriterOpener(ShardContext shardContext) {
    Thread thread = new Thread(new Runnable() {

      @Override
      public void run() {
        try {
          _writer.set(new BlurIndexWriter(_directory, _conf.clone()));
          synchronized (_writer) {
            _writer.notify();
          }
          _indexImporter = new IndexImporter(BlurIndexSimpleWriter.this, _shardContext, TimeUnit.SECONDS, 10);
        } catch (IOException e) {
          LOG.error("Unknown error on index writer open.", e);
        }
      }
    });
    thread.setName("Writer Opener for Table [" + shardContext.getTableContext().getTable() + "] Shard ["
        + shardContext.getShard() + "]");
    thread.setDaemon(true);
    return thread;
  }

  @Override
  public IndexSearcherClosable getIndexSearcher() throws IOException {
    final IndexReader indexReader;
    _indexRefreshReadLock.lock();
    try {
      indexReader = _indexReader.get();
      indexReader.incRef();
    } finally {
      _indexRefreshReadLock.unlock();
    }
    if (indexReader instanceof ExitableReader) {
      ((ExitableReader) indexReader).reset();
    }
    return new IndexSearcherClosable(indexReader, _searchThreadPool) {

      private boolean _closed;

      @Override
      public Directory getDirectory() {
        return _directory;
      }

      @Override
      public synchronized void close() throws IOException {
        if (!_closed) {
          indexReader.decRef();
          _closed = true;
        } else {
          // Not really sure why some indexes get closed called twice on them.
          // This is in place to log it.
          if (LOG.isDebugEnabled()) {
            LOG.debug("Searcher already closed [{0}].", new Throwable(), this);
          }
        }
      }
    };
  }

  private void waitUntilNotNull(AtomicReference<?> ref) {
    while (true) {
      Object object = ref.get();
      if (object != null) {
        return;
      }
      synchronized (ref) {
        try {
          ref.wait(TimeUnit.SECONDS.toMillis(1));
        } catch (InterruptedException e) {
          return;
        }
      }
    }
  }

  @Override
  public void close() throws IOException {
    _isClosed.set(true);
    IOUtils.cleanup(LOG, _indexImporter, _mutationQueueProcessor, _writer.get(), _indexReader.get());
  }

  @Override
  public void refresh() throws IOException {

  }

  @Override
  public AtomicBoolean isClosed() {
    return _isClosed;
  }

  @Override
  public synchronized void optimize(final int numberOfSegmentsPerShard) throws IOException {
    final String table = _tableContext.getTable();
    final String shard = _shardContext.getShard();
    if (_optimizeThread != null && _optimizeThread.isAlive()) {
      LOG.info("Already running an optimize on table [{0}] shard [{1}]", table, shard);
      return;
    }
    _optimizeThread = new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          waitUntilNotNull(_writer);
          BlurIndexWriter writer = _writer.get();
          writer.forceMerge(numberOfSegmentsPerShard, true);
          _writeLock.lock();
          try {
            commit();
          } finally {
            _writeLock.unlock();
          }
        } catch (Exception e) {
          LOG.error("Unknown error during optimize on table [{0}] shard [{1}]", e, table, shard);
        }
      }
    });
    _optimizeThread.setDaemon(true);
    _optimizeThread.setName("Optimize table [" + table + "] shard [" + shard + "]");
    _optimizeThread.start();
  }

  @Override
  public void createSnapshot(String name) throws IOException {
    _writeLock.lock();
    try {
      _snapshotIndexDeletionPolicy.createSnapshot(name, _indexReader.get(), _context);
    } finally {
      _writeLock.unlock();
    }
  }

  @Override
  public void removeSnapshot(String name) throws IOException {
    _writeLock.lock();
    try {
      _snapshotIndexDeletionPolicy.removeSnapshot(name, _context);
    } finally {
      _writeLock.unlock();
    }
  }

  @Override
  public List<String> getSnapshots() throws IOException {
    return new ArrayList<String>(_snapshotIndexDeletionPolicy.getSnapshots());
  }

  private void commit() throws IOException {
    Tracer trace1 = Trace.trace("prepareCommit");
    waitUntilNotNull(_writer);
    BlurIndexWriter writer = _writer.get();
    writer.prepareCommit();
    trace1.done();

    Tracer trace2 = Trace.trace("commit");
    writer.commit();
    trace2.done();

    Tracer trace3 = Trace.trace("index refresh");
    DirectoryReader currentReader = _indexReader.get();
    DirectoryReader newReader = DirectoryReader.openIfChanged(currentReader);
    if (newReader == null) {
      LOG.debug("Reader should be new after commit for table [{0}] shard [{1}].", _tableContext.getTable(),
          _shardContext.getShard());
    } else {
      DirectoryReader reader = wrap(newReader);
      _indexRefreshWriteLock.lock();
      try {
        _indexReader.set(reader);
      } finally {
        _indexRefreshWriteLock.unlock();
      }
      _indexCloser.close(currentReader);
    }
    trace3.done();
  }

  @Override
  public void process(IndexAction indexAction) throws IOException {
    _writesWaiting.incrementAndGet();
    _writeLock.lock();
    _writesWaiting.decrementAndGet();
    indexAction.setWritesWaiting(_writesWaiting);
    waitUntilNotNull(_writer);
    BlurIndexWriter writer = _writer.get();
    IndexSearcherClosable indexSearcher = null;
    try {
      indexSearcher = getIndexSearcher();
      indexAction.performMutate(indexSearcher, writer);
      indexAction.doPreCommit(indexSearcher, writer);
      commit();
      indexAction.doPostCommit(writer);
    } catch (Exception e) {
      indexAction.doPreRollback(writer);
      writer.rollback();
      openWriter();
      indexAction.doPostRollback(writer);
      throw new IOException("Unknown error during mutation", e);
    } finally {
      if (indexSearcher != null) {
        indexSearcher.close();
      }
      _writeLock.unlock();
    }
  }

  public Path getSnapshotsDirectoryPath() {
    return _snapshotIndexDeletionPolicy.getSnapshotsDirectoryPath();
  }

  @Override
  public void enqueue(List<RowMutation> mutations) throws IOException {
    startQueueIfNeeded();
    try {
      for (RowMutation mutation : mutations) {
        _queue.put(mutation);
      }
      synchronized (_queue) {
        _queue.notifyAll();
      }
    } catch (InterruptedException e) {
      throw new IOException(e);
    }
  }

  private void startQueueIfNeeded() {
    _mutationQueueProcessor.startIfNotRunning();
  }

}
TOP

Related Classes of org.apache.blur.manager.writer.BlurIndexSimpleWriter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.