All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bboxdb.storage.sstable.reader.SSTableKeyIndexReader Maven / Gradle / Ivy

There is a newer version: 1.0.0-rc3
Show newest version
/*******************************************************************************
 *
 *    Copyright (C) 2015-2018 the BBoxDB project
 *  
 *    Licensed 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.bboxdb.storage.sstable.reader;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

import org.bboxdb.storage.StorageManagerException;
import org.bboxdb.storage.entity.Tuple;
import org.bboxdb.storage.sstable.SSTableConst;
import org.bboxdb.storage.sstable.SSTableHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

public class SSTableKeyIndexReader extends AbstractTableReader implements Iterable {
	
	/**
	 * The corresponding sstable reader
	 */
	protected final SSTableReader sstableReader;
	
	/**
	 * The key cache 
	 */
	protected LoadingCache keyCache;
	
	/**
	 * The Logger
	 */
	private static final Logger logger = LoggerFactory.getLogger(SSTableKeyIndexReader.class);

	public SSTableKeyIndexReader(final SSTableReader sstableReader) throws StorageManagerException {
		super(sstableReader.getDirectory(), sstableReader.getName(), sstableReader.getTablebumber());
		this.sstableReader = sstableReader;
	}

	@Override
	public void init() {
		super.init();
		logger.debug("Opened index for relation: {} with {} entries", name.getFullname(), getNumberOfEntries());
	}

	/**
	 * Active the key cache with the given capacity
	 * @param elements
	 */
	public void activateKeyCache(final int elements) {
		
		if(keyCache != null) {
			throw new RuntimeException("Keycache was already be initiliazed");
		}
		
		// Don't activate the cache
		if(elements == 0) {
			return;
		}
		
		keyCache = CacheBuilder.newBuilder()
				.maximumSize(elements)
				.build(new CacheLoader() {

			@Override
			public String load(final Long tupleNumber) throws Exception {
				return readKeyFromBytePos(tupleNumber);
			}
			
		});
	}
	
	/**
	 * Scan the index file for the tuple position
	 * @param key
	 * @return
	 * @throws StorageManagerException 
	 */
	public List getPositionsForTuple(final String key) throws StorageManagerException {

		try {
			int firstEntry = 0;
			int lastEntry = getNumberOfEntries() - 1;
			
			// Check key is > then first value
			final String firstValue = getKeyForIndexEntry(firstEntry);
			if(firstValue.equals(key)) {
				return fillKeyPositionArrayFromIndexEntry(key, firstEntry);
			}
			
			// Not found
			if(firstValue.compareTo(key) > 0) {
				return new ArrayList<>();
			}
			
			// Check if key is < then first value
			final String lastValue = getKeyForIndexEntry(lastEntry);
			if(lastValue.equals(key)) {
				return fillKeyPositionArrayFromIndexEntry(key, lastEntry);
			}
			
			// Not found
			if(lastValue.compareTo(key) < 0) {
				return new ArrayList<>();
			}

			// Binary search for key
			do {
				int curEntry = (int) ((lastEntry - firstEntry) / 2.0) + firstEntry;
				
/*				if(logger.isDebugEnabled()) {
					logger.debug("Low: " + firstEntry + " Up: " + lastEntry + " Pos: " + curEntry);
				}*/
				
				final String curEntryValue = getKeyForIndexEntry(curEntry);
				
				if(curEntryValue.equals(key)) {
					return fillKeyPositionArrayFromIndexEntry(key, curEntry);
				}
				
				if(key.compareTo(curEntryValue) > 0) {
					firstEntry = curEntry + 1;
				} else {
					lastEntry = curEntry - 1;
				}
				
			} while(firstEntry <= lastEntry);

		} catch (IOException e) {
			throw new StorageManagerException("Error while reading index file", e);
		}
		
		// Not found during binary scan
		return new ArrayList<>();
	}
	
	/**
	 * The SSTable can contain duplicates, so we nee to scan up and down from 
	 * the given position to retrive all keys
	 * 
	 * @param indexEntry
	 * @throws StorageManagerException 
	 * @throws IOException 
	 */
	protected List fillKeyPositionArrayFromIndexEntry(final String key, final int indexEntry) 
			throws IOException, StorageManagerException {
		
		final List resultList = new ArrayList<>();
		final int lastEntry = getNumberOfEntries() - 1;

		resultList.add(indexEntry);
		
		// Scan upper index entries
		int indexEntryTest = indexEntry + 1;
		while(indexEntryTest <= lastEntry) {
			final String curEntryValue = getKeyForIndexEntry(indexEntryTest);
			
			if(curEntryValue.equals(key)) {
				resultList.add(indexEntryTest);
			} else {
				break;
			}
			
			indexEntryTest++;
		}
		
		// Scan lower index entries
		indexEntryTest = indexEntry - 1;
				
		while(indexEntryTest >= 0) {
			final String curEntryValue = getKeyForIndexEntry(indexEntryTest);
			
			if(curEntryValue.equals(key)) {
				resultList.add(indexEntryTest);
			} else {
				break;
			}
			
			indexEntryTest--;
		}
		
		// Convert index positions
		return resultList.stream()
			.map((e) -> convertEntryToPosition(e))
			.collect(Collectors.toList());
	}

	/**
	 * Get the string key for index entry
	 * @param entry
	 * @return
	 * @throws IOException
	 * @throws StorageManagerException 
	 */
	public String getKeyForIndexEntry(final long entry) throws IOException, StorageManagerException {
		
		if(keyCache != null) {
			try {
				return keyCache.get(entry);
			} catch (ExecutionException e) {
				throw new StorageManagerException(e);
			}
		}
		
		return readKeyFromBytePos(entry);
	}

	/**
	 * Read the key from byte position
	 * @param entry
	 * @return
	 * @throws IOException
	 */
	protected String readKeyFromBytePos(final long entry) throws IOException {
		final int position = convertEntryToPosition(entry);
		return sstableReader.decodeOnlyKeyFromTupleAtPosition(position);
	}
	
	/**
	 * Get the tuple at the given position
	 * @param entry
	 * @return
	 * @throws IOException
	 * @throws StorageManagerException 
	 */
	public Tuple getTupleForIndexEntry(final long entry) throws IOException, StorageManagerException {
		final int position = convertEntryToPosition(entry);
		return sstableReader.getTupleAtPosition(position);
	}

	/**
	 * Convert the index entry to index file position
	 * @param entry
	 * @return
	 */
	protected synchronized int convertEntryToPosition(final long entry) {
		
		// Memory was unmapped
		if(memory == null) {
			return -1;
		}
		
		final byte[] magicBytes = getMagicBytes();
		
		memory.position((int) ((entry * SSTableConst.INDEX_ENTRY_BYTES) + magicBytes.length));
		int position = memory.getInt();
		return position;
	}

	/**
	 * Get the total number of entries
	 * @return
	 */
	public int getNumberOfEntries() {
		try {
			if(fileChannel == null) {
				logger.warn("getNumberOfEntries() called on closed sstableindexreader for relation: {}", name);
				return 0;
			}
			
			final byte[] magicBytes = getMagicBytes();
			
			return (int) ((fileChannel.size() - magicBytes.length) / SSTableConst.INDEX_ENTRY_BYTES);
		} catch (IOException e) {
			logger.error("IO Exception while reading from index", e);
		}
		
		return 0;
	}

	/**
	 * Getter for sstable reader
	 * @return
	 */
	public SSTableReader getSstableReader() {
		return sstableReader;
	}
	
	/**
	 * Iterate over the tuples in the sstable
	 */
	@Override
	public Iterator iterator() {
		
		return new Iterator() {

			protected int entry = 0;
			protected int lastEntry = getNumberOfEntries() - 1;
			
			@Override
			public boolean hasNext() {
				return entry <= lastEntry;
			}

			@Override
			public Tuple next() {
				
				if(entry > lastEntry) {
					throw new IllegalStateException("Requesting wrong position: " + entry + " of " + lastEntry);
				}
				
				try {
					final Tuple tuple = sstableReader.getTupleAtPosition(convertEntryToPosition(entry));
					entry++;
					return tuple;
				} catch (StorageManagerException e) {
					logger.error("Got exception while iterating (requesting entry " + (entry - 1) + " of " + lastEntry + ")", e);
				}
								
				return null;
			}

			@Override
			public void remove() {
				throw new IllegalStateException("Remove is not supported");
			}
		};
	}
	
	@Override
	public String getServicename() {
		return "SSTable key index reader";
	}
	
	/**
	 * Construct the filename of the index file
	 * @param sstableFacades
	 * @return
	 */
	@Override
	protected File constructFileToRead() {
		final String filename = SSTableHelper.getSSTableIndexFilename(directory, name, tablebumber);
		return new File(filename);
	}

	@Override
	protected byte[] getMagicBytes() {
		return SSTableConst.MAGIC_BYTES_INDEX;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy