Package proj.zoie.hourglass.impl

Source Code of proj.zoie.hourglass.impl.Hourglass

package proj.zoie.hourglass.impl;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import javax.management.NotCompliantMBeanException;
import javax.management.StandardMBean;

import org.apache.log4j.Logger;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.store.Directory;

import proj.zoie.api.DefaultDirectoryManager;
import proj.zoie.api.DirectoryManager;
import proj.zoie.api.DocIDMapper;
import proj.zoie.api.Zoie;
import proj.zoie.api.ZoieException;
import proj.zoie.api.ZoieMultiReader;
import proj.zoie.api.indexing.IndexReaderDecorator;
import proj.zoie.api.indexing.ZoieIndexableInterpreter;
import proj.zoie.hourglass.mbean.HourglassAdmin;
import proj.zoie.hourglass.mbean.HourglassAdminMBean;
import proj.zoie.impl.indexing.ZoieConfig;
import proj.zoie.impl.indexing.ZoieSystem;

public class Hourglass<R extends IndexReader, D> implements Zoie<R, D> {
  public static final Logger log = Logger.getLogger(Hourglass.class);
  private final HourglassDirectoryManagerFactory _dirMgrFactory;
  private final ZoieIndexableInterpreter<D> _interpreter;
  private final IndexReaderDecorator<R> _decorator;
  private final ZoieConfig _zConfig;
  private volatile ZoieSystem<R, D> _currentZoie;
  private volatile boolean _isShutdown = false;
  final ReentrantReadWriteLock _shutdownLock = new ReentrantReadWriteLock();
  private final ReentrantLock _consumeLock = new ReentrantLock();
  private final HourglassReaderManager<R, D> _readerMgr;
  private volatile String _currentVersion = null;
  private long _freshness = 1000;
  final HourGlassScheduler _scheduler;
  public volatile long SLA = 4; // getIndexReaders should return in 4ms or a warning is logged
  @SuppressWarnings("rawtypes")
  private List<HourglassListener> _hourglassListeners;

  @SuppressWarnings({ "rawtypes" })
  public Hourglass(HourglassDirectoryManagerFactory dirMgrFactory,
      ZoieIndexableInterpreter<D> interpreter, IndexReaderDecorator<R> readerDecorator,
      ZoieConfig zoieConfig, List<HourglassListener> hourglassListeners) {
    _zConfig = zoieConfig;
    _dirMgrFactory = dirMgrFactory;
    if (hourglassListeners == null) {
      hourglassListeners = Collections.emptyList();
    } else {
      hourglassListeners = new CopyOnWriteArrayList<HourglassListener>(hourglassListeners);
    }
    _hourglassListeners = hourglassListeners;
    _scheduler = _dirMgrFactory.getScheduler();
    _dirMgrFactory.clearRecentlyChanged();
    _interpreter = interpreter;
    _decorator = readerDecorator;
    List<ZoieMultiReader<R>> archives;
    List<ZoieSystem<R, D>> archiveZoies;
    if (_dirMgrFactory.getScheduler().isAppendOnly()) {
      archives = loadArchives();
      archiveZoies = Collections.emptyList();
    } else {
      archives = Collections.emptyList();
      archiveZoies = loadArchiveZoies();
    }
    _readerMgr = new HourglassReaderManager<R, D>(this, _dirMgrFactory, _decorator, archives,
        archiveZoies, hourglassListeners);
    _currentVersion = _dirMgrFactory.getArchivedVersion();
    _currentZoie = _readerMgr.retireAndNew(null);
    _currentZoie.start();
    _freshness = zoieConfig.getFreshness();
    log.info("start Hourglass at version: " + _currentVersion);
  }

  @SuppressWarnings("rawtypes")
  public Hourglass(HourglassDirectoryManagerFactory dirMgrFactory,
      ZoieIndexableInterpreter<D> interpreter, IndexReaderDecorator<R> readerDecorator,
      ZoieConfig zoieConfig) {
    this(dirMgrFactory, interpreter, readerDecorator, zoieConfig, Collections
        .<HourglassListener> emptyList());
  }

  @SuppressWarnings({ "rawtypes" })
  public Hourglass(HourglassDirectoryManagerFactory dirMgrFactory,
      ZoieIndexableInterpreter<D> interpreter, IndexReaderDecorator<R> readerDecorator,
      ZoieConfig zoieConfig, HourglassListener hourglassListener) {
    this(dirMgrFactory, interpreter, readerDecorator, zoieConfig, Arrays.asList(hourglassListener));
  }

  protected List<ZoieSystem<R, D>> loadArchiveZoies() {
    List<ZoieSystem<R, D>> archives = new ArrayList<ZoieSystem<R, D>>();
    long t0 = System.currentTimeMillis();
    List<File> dirs = _dirMgrFactory.getAllArchivedDirs();
    for (File dir : dirs) {
      try {
        DirectoryManager dirMgr = new DefaultDirectoryManager(dir, _dirMgrFactory.getMode());
        ZoieSystem<R, D> zoie = new ZoieSystem<R, D>(dirMgr, _interpreter, _decorator, _zConfig);
        zoie.start();
        archives.add(zoie);
      } catch (Exception e) {
        log.error("Load index: " + dir + " failed.", e);
      }
    }
    log.info("load " + dirs.size() + " archived indices of " + getSizeBytes() + " bytes in "
        + (System.currentTimeMillis() - t0) + "ms");
    return archives;
  }

  protected List<ZoieMultiReader<R>> loadArchives() {
    List<ZoieMultiReader<R>> archives = new ArrayList<ZoieMultiReader<R>>();
    long t0 = System.currentTimeMillis();
    List<Directory> dirs = _dirMgrFactory.getAllArchivedDirectories();
    for (Directory dir : dirs) {
      DirectoryReader reader;
      try {
        reader = DirectoryReader.open(dir);
        ZoieMultiReader<R> zoiereader = new ZoieMultiReader<R>(reader, _decorator);

        // Initialize docIdMapper
        DocIDMapper mapper = _zConfig.getDocidMapperFactory().getDocIDMapper(zoiereader);
        zoiereader.setDocIDMapper(mapper);

        archives.add(zoiereader);
      } catch (CorruptIndexException e) {
        log.error("corruptedIndex", e);
      } catch (IOException e) {
        log.error("IOException", e);
      }
    }
    log.info("load " + dirs.size() + " archived indices of " + getSizeBytes() + " bytes in "
        + (System.currentTimeMillis() - t0) + "ms");
    return archives;
  }

  ZoieSystem<R, D> createZoie(DirectoryManager dirmgr) {
    return new ZoieSystem<R, D>(dirmgr, _interpreter, _decorator, _zConfig);
  }

  public ZoieConfig getzConfig() {
    return _zConfig;
  }

  public ZoieSystem<R, D> getCurrentZoie() {
    return _currentZoie;
  }

  public HourglassDirectoryManagerFactory getDirMgrFactory() {
    return _dirMgrFactory;
  }

  /*
   * (non-Javadoc)
   * @see proj.zoie.api.IndexReaderFactory#getAnalyzer()
   */
  @Override
  public Analyzer getAnalyzer() {
    return _zConfig.getAnalyzer();
  }

  /**
   * return a list of ZoieMultiReaders. These readers are reference counted and this method
   * should be used in pair with returnIndexReaders(List<ZoieMultiReader<R>> readers) {@link #returnIndexReaders(List)}.
   * It is typical that we create a MultiReader from these readers. When creating MultiReader, it should be created with
   * the closeSubReaders parameter set to false in order to do reference counting correctly.
   * <br> If this indexing system is already shut down, then we return an empty list.
   * @see proj.zoie.hourglass.impl.Hourglass#returnIndexReaders(List)
   * @see proj.zoie.api.IndexReaderFactory#getIndexReaders()
   */
  @Override
  public List<ZoieMultiReader<R>> getIndexReaders() throws IOException {
    long t0 = System.currentTimeMillis();
    try {
      _shutdownLock.readLock().lock();
      if (_isShutdown) {
        log.warn("System already shut down. No search request allowed.");
        List<ZoieMultiReader<R>> list = new ArrayList<ZoieMultiReader<R>>();
        return list;// if already shutdown, return an empty list
      }
      try {
        cacheLock.lock();
        if (System.currentTimeMillis() - lastupdate > _freshness) {
          updateCachedReaders();
        }
        List<ZoieMultiReader<R>> rlist = list;
        for (ZoieMultiReader<R> r : rlist) {
          r.incZoieRef();
        }
        t0 = System.currentTimeMillis() - t0;
        if (t0 > SLA) {
          log.warn("getIndexReaders returned in " + t0 + "ms more than " + SLA + "ms");
        }
        return rlist;
      } finally {
        cacheLock.unlock();
      }
    } finally {
      _shutdownLock.readLock().unlock();
    }
  }

  /**
   * not thread safe. should be properly lock. Right now we have two places to use it
   * and locked by the shutdown lock. If it gets more complicated, we should use separate
   * lock.
   * @throws IOException
   */
  private void updateCachedReaders() throws IOException {
    if (log.isDebugEnabled()) {
      log.debug("updating reader cache");
    }
    List<ZoieMultiReader<R>> olist = list;
    returnIndexReaders(olist);
    if (log.isDebugEnabled()) {
      log.debug("getting new reader from reader cache");
    }
    list = _readerMgr.getIndexReaders();
    if (log.isDebugEnabled()) {
      log.debug("reader updated with size: " + list.size());
    }
    lastupdate = System.currentTimeMillis();
  }

  /**
   * not thread safe. should be properly lock. Right now we have two places to use it
   * and locked by the shutdown lock. If it gets more complicated, we should use separate
   * lock.
   */
  private void clearCachedReaders() {
    List<ZoieMultiReader<R>> olist = list;
    returnIndexReaders(olist);
    list = null;
    lastupdate = 0;
  }

  private volatile long lastupdate = 0;
  private volatile List<ZoieMultiReader<R>> list = new ArrayList<ZoieMultiReader<R>>();
  private final ReentrantLock cacheLock = new ReentrantLock();

  /*
   * (non-Javadoc)
   * @see proj.zoie.api.IndexReaderFactory#returnIndexReaders(java.util.List)
   */
  @Override
  public void returnIndexReaders(List<ZoieMultiReader<R>> readers) {
    long t0 = System.currentTimeMillis();
    _currentZoie.returnIndexReaders(readers);
    t0 = System.currentTimeMillis() - t0;
    if (t0 > SLA) {
      log.warn("returnIndexReaders returned in " + t0 + "ms more than " + SLA + "ms");
    }
  }

  private void clearFromArchives(Collection<DataEvent<D>> data) throws ZoieException {
    if (_dirMgrFactory.getScheduler().isAppendOnly()) return;

    if (data != null && data.size() > 0) {
      List<DataEvent<D>> deletes = new ArrayList<DataEvent<D>>(data.size());
      for (DataEvent<D> event : data) {
        deletes.add(new DataEvent<D>(event.getData(), event.getVersion(), true));
      }
      for (ZoieSystem<R, D> zoie : _readerMgr.getArchiveZoies()) {
        zoie.consume(deletes);
      }
    }
  }

  /*
   * (non-Javadoc)
   * @see proj.zoie.api.DataConsumer#consume(java.util.Collection)
   */
  @Override
  public void consume(Collection<DataEvent<D>> data) throws ZoieException {
    try {
      _consumeLock.lock(); // one at a time so we don't mess up during forward rolling.
                           // also we cannot do two consumptions at the same time anyway.
      try {
        _shutdownLock.readLock().lock();
        if (data == null || data.size() == 0) return;
        if (_isShutdown) {
          log.warn("System already shut down. Rejects indexing request.");
          return; // if the system is already shut down, we don't do anything.
        }
        // need to check time boundary. When we hit boundary, we need to trigger DM to
        // use new dir for zoie and the old one will be archive.
        if (!_dirMgrFactory.updateDirectoryManager()) {
          clearFromArchives(data);
          _currentZoie.consume(data);
        } else {
          // new time period
          _currentZoie = _readerMgr.retireAndNew(_currentZoie);
          _currentZoie.start();
          clearFromArchives(data);
          _currentZoie.consume(data);
        }
      } finally {
        _shutdownLock.readLock().unlock();
      }
    } finally {
      _consumeLock.unlock();
    }
  }

  @Override
  public void shutdown() {
    try {
      _shutdownLock.writeLock().lock();
      if (_isShutdown) {
        log.info("system already shut down");
        return;
      }
      _isShutdown = true;
    } finally {
      _shutdownLock.writeLock().unlock();
    }
    clearCachedReaders();
    _readerMgr.shutdown();

    log.info("shut down complete.");
  }

  /*
   * (non-Javadoc)
   * @see proj.zoie.api.DataConsumer#getVersion()
   */
  @Override
  public String getVersion() {
    // _currentVersion = Math.max(_currentVersion, _currentZoie.getCurrentVersion());
    if (_currentZoie.getCurrentVersion() != null) {
      if (_currentVersion == null) {
        return _currentVersion = _currentZoie.getCurrentVersion();
      } else {
        _currentVersion = _zConfig.getVersionComparator().compare(_currentZoie.getCurrentVersion(),
          _currentVersion) < 0 ? _currentVersion : _currentZoie.getCurrentVersion();
      }
    }

    return _currentVersion;
  }

  /*
   * (non-Javadoc)
   * @see proj.zoie.api.DataConsumer#getVersionComparator()
   */
  @Override
  public Comparator<String> getVersionComparator() {
    return _zConfig.getVersionComparator();
  }

  public long getSizeBytes() {
    return _dirMgrFactory.getDiskIndexSizeBytes();
  }

  @Override
  public void syncWithVersion(long timeInMillis, String version) throws ZoieException {
    if (_currentZoie != null) {
      _currentZoie.syncWithVersion(timeInMillis, version);
    }
  }

  @Override
  public StandardMBean getStandardMBean(String name) {
    if (name.equals(HOURGLASSADMIN)) {
      try {
        return new StandardMBean(getAdminMBean(), HourglassAdminMBean.class);
      } catch (NotCompliantMBeanException e) {
        log.info(e);
        return null;
      }
    }
    return null;
  }

  @Override
  public HourglassAdminMBean getAdminMBean() {
    return new HourglassAdmin(this);
  }

  public static String HOURGLASSADMIN = "hourglass-admin";

  @Override
  public String[] getStandardMBeanNames() {
    return new String[] { HOURGLASSADMIN };
  }

  @Override
  public void start() {
    log.info("starting Hourglass... already done due by auto-start");
  }

  @Override
  public void flushEvents(long timeout) throws ZoieException {
    _currentZoie.flushEvents(timeout);
  }

  @Override
  public String getCurrentReaderVersion() {
    return _currentZoie == null ? null : _currentZoie.getCurrentReaderVersion();
  }

  public void addHourglassListener(HourglassListener<R, D> hourglassListener) {
    if (hourglassListener != null) {
      _hourglassListeners.add(hourglassListener);
    }
  }
}
TOP

Related Classes of proj.zoie.hourglass.impl.Hourglass

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.