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

org.bboxdb.storage.sstable.reader.SSTableFacade 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.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.bboxdb.commons.math.Hyperrectangle;
import org.bboxdb.misc.BBoxDBException;
import org.bboxdb.misc.BBoxDBService;
import org.bboxdb.storage.BloomFilterBuilder;
import org.bboxdb.storage.StorageManagerException;
import org.bboxdb.storage.entity.Tuple;
import org.bboxdb.storage.entity.TupleStoreMetaData;
import org.bboxdb.storage.entity.TupleStoreName;
import org.bboxdb.storage.sstable.SSTableHelper;
import org.bboxdb.storage.sstable.spatialindex.SpatialIndexEntry;
import org.bboxdb.storage.sstable.spatialindex.SpatialIndexReader;
import org.bboxdb.storage.sstable.spatialindex.SpatialIndexReaderFactory;
import org.bboxdb.storage.tuplestore.ReadOnlyTupleStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.hash.BloomFilter;

public class SSTableFacade implements BBoxDBService, ReadOnlyTupleStore {
	 
	/**
	 * The name of the table
	 */
	protected final TupleStoreName tablename;
	
	/**
	 * The Directory for the SSTables
	 */
	protected final String directory;
	
	/**
	 * The SStable reader
	 */
	protected final SSTableReader ssTableReader;
	
	/**
	 * The SSTable Key index reader
	 */
	protected final SSTableKeyIndexReader ssTableKeyIndexReader;
	
	/**
	 * The metadata of the sstable
	 */
	protected final TupleStoreMetaData ssTableMetadata;
	
	/**
	 * The spatial index
	 */
	protected SpatialIndexReader spatialIndex;
	
	/**
	 * The Bloom filter
	 */
	protected BloomFilter bloomfilter;
	
	/**
	 * The number of the table
	 */
	protected final int tablenumber;
	
	/**
	 * The usage counter
	 */
	protected final AtomicInteger usage;
	
	/**
	 * Delete file on close
	 */
	protected volatile boolean deleteOnClose; 
	
	/**
	 * The key cache size
	 */
	protected int keyCacheElements;
	
	/**
	 * The Logger
	 */
	private static final Logger logger = LoggerFactory.getLogger(SSTableFacade.class);
	

	public SSTableFacade(final String directory, final TupleStoreName tablename, final int tablenumber, 
			final int keyCacheElements) throws StorageManagerException {
		super();
		this.tablename = tablename;
		this.directory = directory;
		this.tablenumber = tablenumber;
		
		ssTableReader = new SSTableReader(directory, tablename, tablenumber);
		ssTableKeyIndexReader = new SSTableKeyIndexReader(ssTableReader);
		
		// Meta data
		final File metadataFile = getMetadataFile(directory, tablename, tablenumber);
		ssTableMetadata = TupleStoreMetaData.importFromYamlFile(metadataFile);
		
		this.usage = new AtomicInteger(0);
		deleteOnClose = false;
		this.keyCacheElements = keyCacheElements;
	}

	/**
	 * Get the spatial index file
	 * @param directory
	 * @param tablename
	 * @param tablenumber
	 * @return
	 */
	protected File getSpatialIndexFile(final String directory, final TupleStoreName tablename, final int tablenumber) {
		final String spatialIndexFileName = SSTableHelper.getSSTableSpatialIndexFilename(directory, tablename, tablenumber);
		final File spatialIndexFile = new File(spatialIndexFileName);
		return spatialIndexFile;
	}

	/**
	 * Get the bloomfilter file
	 * @param directory
	 * @param tablename
	 * @param tablenumber
	 * @return
	 */
	protected File getBloomFilterFile(final String directory, final TupleStoreName tablename, final int tablenumber) {
		final String bloomFilterFileName = SSTableHelper.getSSTableBloomFilterFilename(directory, tablename, tablenumber);
		final File bloomFilterFile = new File(bloomFilterFileName);
		return bloomFilterFile;
	}
	
	/**
	 * Load the spatial index from file
	 * @throws StorageManagerException 
	 */
	protected void loadSpatialIndex(final File spatialIndexFile) throws StorageManagerException {
		if(! spatialIndexFile.exists()) {
			throw new StorageManagerException("The spatial index does not exists: " + spatialIndexFile);
		}
		
		try (   final RandomAccessFile randomAccessFile = new RandomAccessFile(spatialIndexFile, "r") 
			) {
			
			spatialIndex = SpatialIndexReaderFactory.getInstance();
			spatialIndex.readFromFile(randomAccessFile);
			
		} catch (Exception e) {
			throw new StorageManagerException(e);
		}
	}
	
	/**
	 * Load the boom filter from file
	 * @param bloomFilterFile
	 */
	protected void loadBloomFilter(final File bloomFilterFile) {
		
		if(! bloomFilterFile.exists()) {
			logger.warn("Bloom filter file {} does not exist, working without bloom filter");
			bloomfilter = null;
			return;
		}
	
		try {
			bloomfilter = BloomFilterBuilder.loadBloomFilterFromFile(bloomFilterFile);
		} catch (IOException e) {
			logger.warn("Unable to load the bloom filter", e);
			bloomfilter = null;
		}	
	}

	/**
	 * Calculate the name of the metadata file
	 * @param directory
	 * @param tablename
	 * @param tablenumber
	 * @return
	 */
	protected File getMetadataFile(final String directory,
			final TupleStoreName tablename, final int tablenumber) {
		final String metadatafile = SSTableHelper.getSSTableMetadataFilename(directory, tablename, tablenumber);
		return new File(metadatafile);
	}
	
	@Override
	public void init() throws InterruptedException, BBoxDBException {
		
		try {
			if(ssTableReader == null || ssTableKeyIndexReader == null) {
				logger.warn("init called but sstable reader or index reader is null");
				return;
			}
			
			ssTableReader.init();
			
			ssTableKeyIndexReader.init();
			ssTableKeyIndexReader.activateKeyCache(keyCacheElements);
			
			// Spatial index
			final File spatialIndexFile = getSpatialIndexFile(directory, tablename, tablenumber);
			loadSpatialIndex(spatialIndexFile); 
			
			// Bloom filter
			final File bloomFilterFile = getBloomFilterFile(directory, tablename, tablenumber);
			loadBloomFilter(bloomFilterFile);
		} catch (StorageManagerException e) {
			throw new BBoxDBException(e);
		}
	}

	@Override
	public void shutdown() {
		if(ssTableKeyIndexReader != null) {
			ssTableKeyIndexReader.shutdown();
		}
		
		if(ssTableReader != null) {
			ssTableReader.shutdown();
		}
		
		if(spatialIndex != null) {
			spatialIndex.close();
		}
	}

	@Override
	public String getServicename() {
		return "SSTable facade for: " + tablename + " " + tablenumber;
	}

	@Override
	public String toString() {
		return "SSTableFacade [name=" + tablename.getFullname() + ", directory=" + directory
				+ ", tablenumber=" + tablenumber + ", oldestTupleTimestamp="
				+ getOldestTupleVersionTimestamp() + ", newestTupleTimestamp="
				+ getNewestTupleVersionTimestamp() + ", deleteOnClose=" + deleteOnClose + "]";
	}

	public SSTableReader getSsTableReader() {
		return ssTableReader;
	}

	public SSTableKeyIndexReader getSsTableKeyIndexReader() {
		return ssTableKeyIndexReader;
	}
	

	/* (non-Javadoc)
	 * @see org.bboxdb.storage.sstable.reader.Acquirable#deleteOnClose()
	 */
	@Override
	public void deleteOnClose() {
		deleteOnClose = true;
		testFileDelete();
	}
	
	/* (non-Javadoc)
	 * @see org.bboxdb.storage.sstable.reader.Acquirable#acquire()
	 */
	@Override
	public boolean acquire() {
		
		// We are closing this instance
		if(deleteOnClose == true) {
			return false;
		}
		
		usage.incrementAndGet();
		return true;
	}
	
	/* (non-Javadoc)
	 * @see org.bboxdb.storage.sstable.reader.Acquirable#release()
	 */
	@Override
	public void release() {
		
		assert (usage.get() > 0);
		
		usage.decrementAndGet();
		
		testFileDelete();
	}

	/**
	 * Delete the file if possible
	 */
	protected void testFileDelete() {
		if(deleteOnClose && usage.get() == 0) {
			logger.info("Delete service facade for: {} / {}", tablename.getFullname(), tablenumber);
			
			shutdown();
			
			// Delete key index reader
			if(ssTableKeyIndexReader != null) {
				ssTableKeyIndexReader.delete();
			}
			
			// Delete reader
			if(ssTableReader != null) {
				ssTableReader.delete();
			}
			
			// Delete spatial index
			final File spatialIndexFile = getSpatialIndexFile(directory, tablename, tablenumber);
			spatialIndexFile.delete();
			
			// Delete bloom filter
			final File bloomFilterFile = getBloomFilterFile(directory, tablename, tablenumber);
			bloomFilterFile.delete();
			
			// Delete metadata
			final File metadataFile = getMetadataFile(directory, tablename, tablenumber);
			metadataFile.delete();
		}
	}

	@Override
	public String getInternalName() {
		return tablename.getFullname() + " / " + tablenumber;
	}

	@Override
	public TupleStoreName getTupleStoreName() {
		return tablename;
	}
	
	public String getDirectory() {
		return directory;
	}

	public int getTablebumber() {
		return tablenumber;
	}

	public AtomicInteger getUsage() {
		return usage;
	}

	public TupleStoreMetaData getSsTableMetadata() {
		return ssTableMetadata;
	}

	@Override
	public long getOldestTupleVersionTimestamp() {
		return ssTableMetadata.getOldestTupleVersionTimestamp();
	}

	@Override
	public long getNewestTupleVersionTimestamp() {
		return ssTableMetadata.getNewestTupleVersionTimestamp();
	}
	
	@Override
	public long getNewestTupleInsertedTimestamp() {
		return ssTableMetadata.getNewestTupleInsertedTimstamp();
	}

	@Override
	public List get(final String key) throws StorageManagerException {
		
		assert (usage.get() > 0);

		// Check bloom filter first
		if(bloomfilter == null) {
			logger.warn("File {} does not have a bloom filter", tablename);
		} else {
			if(! bloomfilter.mightContain(key)) {
				// Not found
				return new ArrayList<>();
			}
		}
		
		final List resultList = new ArrayList<>();
		final List positions = ssTableKeyIndexReader.getPositionsForTuple(key);
		
		for(final Integer position : positions) {
			resultList.add(ssTableReader.getTupleAtPosition(position));
		}
		
		return resultList;
	}

	@Override
	public Iterator iterator() {
		
		assert (usage.get() > 0);

		return ssTableKeyIndexReader.iterator();
	}

	@Override
	public long getNumberOfTuples() {

		assert (usage.get() > 0);
		
		return ssTableMetadata.getTuples();
	}

	@Override
	public Tuple getTupleAtPosition(final long position) throws StorageManagerException {
		
		assert (usage.get() > 0);
		
		try {
			return ssTableKeyIndexReader.getTupleForIndexEntry(position);
		} catch (IOException e) {
			throw new StorageManagerException(e);
		}
	}
	
	/**
	 * Get the size on disk of the facade 
	 * @return
	 */
	public long getSize() {
		return ssTableKeyIndexReader.getSize() + ssTableReader.getSize();
	}

	@Override
	public Iterator getAllTuplesInBoundingBox(final Hyperrectangle boundingBox) {
		assert (usage.get() > 0);

		List entries;
		
		try {
			entries = spatialIndex.getEntriesForRegion(boundingBox);
		} catch (StorageManagerException e) {
			throw new RuntimeException(e);
		}
		
		final Iterator entryIterator = entries.iterator();
		
		return new Iterator() {

			@Override
			public boolean hasNext() {
				return entryIterator.hasNext();
			}

			@Override
			public Tuple next() {
				final SpatialIndexEntry entry = entryIterator.next();
				final int tuplePosition = entry.getValue();
				
				try {
					return ssTableReader.getTupleAtPosition(tuplePosition);
				} catch (StorageManagerException e) {
					throw new RuntimeException(e);
				}
			}
		};
	}

	@Override
	public boolean isPersistent() {
		return true;
	}
	
	@Override
	public boolean isDeletePending() {
		return deleteOnClose;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy