package com.ctriposs.tsdb.iterator; import java.io.IOException; import java.util.Map.Entry; import java.util.concurrent.ConcurrentSkipListSet; import com.ctriposs.tsdb.ISeekIterator; import com.ctriposs.tsdb.InternalKey; import com.ctriposs.tsdb.common.IFileIterator; import com.ctriposs.tsdb.common.Level; import com.ctriposs.tsdb.common.PureFileStorage; import com.ctriposs.tsdb.manage.FileManager; import com.ctriposs.tsdb.storage.FileMeta; import com.ctriposs.tsdb.util.ByteUtil; public class LevelSeekIterator implements ISeekIterator<InternalKey, byte[]> { private FileManager fileManager; private ConcurrentSkipListSet<IFileIterator<InternalKey, byte[]>> itSet; private Direction direction; private Entry<InternalKey, byte[]> curEntry; private IFileIterator<InternalKey, byte[]> curIt; private long curSeekTime; private InternalKey seekKey; private Level level; public LevelSeekIterator(FileManager fileManager, Level level) { this.fileManager = fileManager; this.level = level; this.direction = Direction.forward; this.curEntry = null; this.curIt = null; this.itSet = null; this.curSeekTime = 0; } @Override public boolean hasNext() { boolean result = false; if(curIt!=null&&curIt.hasNext()){ result = true; }else{ if (itSet != null) { for (IFileIterator<InternalKey, byte[]> it : itSet) { if (it.hasNext()) { result = true; break; } } } if (!result) { curSeekTime += level.getLevelInterval(); try { if(nextIterators(curSeekTime)){ if (null != itSet) { for (IFileIterator<InternalKey, byte[]> it : itSet) { it.seek(seekKey.getCode(), curSeekTime); } findSmallest(); direction = Direction.forward; if(curIt!=null&&curIt.hasNext()){ result = true; } } }else{ return false; } } catch (IOException e) { result = false; throw new RuntimeException(e); } }else{ if(curIt != null){ curIt.next(); } findSmallest(); if(curIt!=null&&curIt.hasNext()){ result = true; } } } return result; } @Override public boolean hasPrev() { boolean result = false; if(curIt!=null&&curIt.hasPrev()){ result = true; }else{ if (itSet != null) { for (IFileIterator<InternalKey, byte[]> it : itSet) { if (it.hasPrev()) { result = true; break; } } } if (!result) { curSeekTime -= level.getLevelInterval(); try { if(prevIterators(curSeekTime)){ if (null != itSet) { for (IFileIterator<InternalKey, byte[]> it : itSet) { if(curEntry != null){ it.seek(seekKey.getCode(), curEntry.getKey().getTime()); }else{ it.seek(seekKey.getCode(), curSeekTime); } } findLargest(); direction = Direction.reverse; if(curIt!=null&&curIt.hasNext()){ result = true; } } }else{ return false; } } catch (IOException e) { result = false; throw new RuntimeException(e); } }else{ if(curIt != null){ curIt.prev(); } findLargest(); if(curIt!=null&&curIt.hasPrev()){ result = true; } } } return result; } @Override public Entry<InternalKey, byte[]> next() { if (direction != Direction.forward) { for (IFileIterator<InternalKey, byte[]> it : itSet) { if (it != curIt) { try { if (it.hasNext()) { it.seek(seekKey.getCode(), curSeekTime); } } catch (IOException e) { throw new RuntimeException(e); } } } findSmallest(); direction = Direction.forward; } curEntry = curIt.next(); findSmallest(); return curEntry; } @Override public Entry<InternalKey, byte[]> prev() { if (direction != Direction.reverse) { if(itSet == null){ try { nextIterators(curEntry.getKey().getTime()); } catch (IOException e) { throw new RuntimeException(e); } } for (IFileIterator<InternalKey, byte[]> it : itSet) { if (curIt != it) { try { if (it.hasNext()) { it.seek(seekKey.getCode(), curSeekTime); } } catch (IOException e) { throw new RuntimeException(e); } } } findLargest(); direction = Direction.reverse; } curEntry = curIt.prev(); findLargest(); return curEntry; } @Override public void seek(String table, String column, long time) throws IOException { int code = ByteUtil.ToInt(fileManager.getCode(table),fileManager.getCode(column)); seek(code, time); } @Override public void seek(int code, long time) throws IOException { seekKey = new InternalKey(code, time); if(!nextIterators(time)){ prevIterators(time); } if (null != itSet) { for (IFileIterator<InternalKey, byte[]> it : itSet) { it.seek(seekKey.getCode(), time); } findSmallest(); curEntry = curIt.current(); direction = Direction.forward; } } private void findSmallest() { if (null != itSet) { IFileIterator<InternalKey, byte[]> smallest = null; for (IFileIterator<InternalKey, byte[]> it : itSet) { if (it.valid()) { if (smallest == null) { smallest = it; } else if (fileManager.compare(smallest.key(), it.key()) > 0) { smallest = it; } else if (fileManager.compare(smallest.key(), it.key()) == 0) { //filter the same key after lower level while (it.hasNext()) { it.next(); int diff = fileManager.compare(smallest.key(),it.key()); if (0 == diff) { continue; } else { break; } } } } } if(smallest != null){ curIt = smallest; } } } private void findLargest() { if (null != itSet) { IFileIterator<InternalKey, byte[]> largest = null; for (IFileIterator<InternalKey, byte[]> it : itSet) { if (it.valid()) { if (largest == null) { largest = it; } else if (fileManager.compare(largest.key(), it.key()) < 0) { largest = it; } else if (fileManager.compare(largest.key(), it.key()) == 0) { //filter the same key after lower level while (it.hasPrev()) { it.prev(); int diff = fileManager.compare(largest.key(),it.key()); if (0 == diff) { continue; } else { break; } } } } } if(largest != null){ curIt = largest; } } } private ConcurrentSkipListSet<IFileIterator<InternalKey, byte[]>> getIterators(long time, boolean isNext) throws IOException { while (true) { Long nearTime = level.nearTime(time, isNext); if(nearTime == null){ break; }else{ curSeekTime = nearTime; ConcurrentSkipListSet<FileMeta> metaSet = level.getFiles(nearTime); if (metaSet != null && metaSet.size() > 0) { ConcurrentSkipListSet<IFileIterator<InternalKey, byte[]>> set = new ConcurrentSkipListSet<IFileIterator<InternalKey, byte[]>>(fileManager.getFileIteratorComparator()); for (FileMeta meta : metaSet) { set.add(new FileSeekIterator(new PureFileStorage(meta.getFile()), meta.getFileNumber())); } return set; } } } return null; } private boolean nextIterators(long time) throws IOException{ ConcurrentSkipListSet<IFileIterator<InternalKey, byte[]>> set = getIterators(time,true); if(set != null){ close(); itSet = set; return true; } return false; } private boolean prevIterators(long time) throws IOException{ ConcurrentSkipListSet<IFileIterator<InternalKey, byte[]>> set = getIterators(time,false); if(set != null){ close(); itSet = set; return true; } return false; } @Override public String table() { if (curEntry != null) { return fileManager.getName(curEntry.getKey().getTableCode()); } return null; } @Override public String column() { if (curEntry != null) { return fileManager.getName(curEntry.getKey().getColumnCode()); } return null; } @Override public long time() { if (curEntry != null) { return curEntry.getKey().getTime(); } return 0; } @Override public byte[] value() throws IOException { if (curEntry != null) { return curEntry.getValue(); } return null; } @Override public boolean valid() { if (curEntry == null) { return false; } else { return true; } } @Override public void close() throws IOException { if (null != itSet) { for (IFileIterator<InternalKey, byte[]> it : itSet) { it.close(); } } } @Override public InternalKey key() { if (curEntry != null) { return curEntry.getKey(); } return null; } @Override public void remove() { throw new UnsupportedOperationException("unsupport remove operation!"); } enum Direction { forward, reverse } @Override public long priority() { return level.getLevelNum(); } }