package com.ctriposs.sdb.table; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel.MapMode; import java.util.Arrays; import com.ctriposs.sdb.utils.MMFUtil; import com.google.common.base.Preconditions; /** * * Memory mapped map table accompanied with bloom filter * * @author bulldog * */ public class MMFMapTable extends AbstractSortedMapTable { protected MappedByteBuffer dataMappedByteBuffer; // Create new public MMFMapTable(String dir, int level, long createdTime, int expectedInsertions, int mergeWays) throws IOException { this(dir, (short)0, level, createdTime, expectedInsertions, mergeWays); } public MMFMapTable(String dir, short shard, int level, long createdTime, int expectedInsertions, int mergeWays) throws IOException { super(dir, shard, level, createdTime, expectedInsertions); int mapDataFileSize = INIT_DATA_FILE_SIZE * mergeWays; dataMappedByteBuffer = this.dataChannel.map(MapMode.READ_WRITE, 0, mapDataFileSize); } // Load existing public MMFMapTable(String dir, String fileName) throws IOException, ClassNotFoundException { super(dir, fileName); int mapDataFileSize = (int) this.dataChannel.size(); dataMappedByteBuffer = this.dataChannel.map(MapMode.READ_WRITE, 0, mapDataFileSize); } public void reMap() throws IOException { super.reMap(); MMFUtil.unmap(dataMappedByteBuffer); this.dataChannel.truncate(toAppendDataFileOffset.get()); dataMappedByteBuffer = this.dataChannel.map(MapMode.READ_ONLY, 0, this.dataChannel.size()); } // for testing public IMapEntry appendNew(byte[] key, byte[] value, long timeToLive) throws IOException { return this.appendNew(key, Arrays.hashCode(key), value, timeToLive, System.currentTimeMillis(), false, false); } @Override public IMapEntry appendNew(byte[] key, int keyHash, byte[] value, long timeToLive, long createdTime, boolean markDelete, boolean compressed) throws IOException { ensureNotClosed(); Preconditions.checkArgument(key != null && key.length > 0, "Key is empty"); Preconditions.checkArgument(value != null && value.length > 0, "value is empty"); Preconditions.checkArgument(this.toAppendIndex.get() < MAX_ALLOWED_NUMBER_OF_ENTRIES, "Exceeded max allowed number of entries(" + MAX_ALLOWED_NUMBER_OF_ENTRIES + ")!"); appendLock.lock(); try { // write index metadata indexBuf.clear(); indexBuf.putLong(IMapEntry.INDEX_ITEM_IN_DATA_FILE_OFFSET_OFFSET, toAppendDataFileOffset.get()); indexBuf.putInt(IMapEntry.INDEX_ITEM_KEY_LENGTH_OFFSET, key.length); indexBuf.putInt(IMapEntry.INDEX_ITEM_VALUE_LENGTH_OFFSET, value.length); indexBuf.putLong(IMapEntry.INDEX_ITEM_TIME_TO_LIVE_OFFSET, timeToLive); indexBuf.putLong(IMapEntry.INDEX_ITEM_CREATED_TIME_OFFSET, createdTime); indexBuf.putInt(IMapEntry.INDEX_ITEM_KEY_HASH_CODE_OFFSET, keyHash); byte status = 1; // mark in use if (markDelete) { status = (byte) (status + 2); // binary 11 } if (compressed && !markDelete) { status = (byte) (status + 4); } indexBuf.put(IMapEntry.INDEX_ITEM_STATUS, status); // mark in use int offsetInIndexFile = INDEX_ITEM_LENGTH * toAppendIndex.get(); this.indexMappedByteBuffer.position(offsetInIndexFile); //indexBuf.rewind(); this.indexMappedByteBuffer.put(indexBuf); // write key/value this.dataMappedByteBuffer.position((int)toAppendDataFileOffset.get()); this.dataMappedByteBuffer.put(ByteBuffer.wrap(key)); this.dataMappedByteBuffer.position((int)toAppendDataFileOffset.get() + key.length); this.dataMappedByteBuffer.put(ByteBuffer.wrap(value)); // update guarded condition this.bloomFilter.put(key); int dataLength = key.length + value.length; // commit/update offset & index toAppendDataFileOffset.addAndGet(dataLength); int appendedIndex = toAppendIndex.get(); toAppendIndex.incrementAndGet(); return new MMFMapEntryImpl(appendedIndex, this.indexMappedByteBuffer, this.dataMappedByteBuffer); } finally { appendLock.unlock(); } } @Override public IMapEntry getMapEntry(int index) { ensureNotClosed(); Preconditions.checkArgument(index >= 0, "index (%s) must be equal to or greater than 0", index); Preconditions.checkArgument(!isEmpty(), "Can't get map entry since the map is empty"); return new MMFMapEntryImpl(index, this.indexMappedByteBuffer, this.dataMappedByteBuffer); } @Override public void close() throws IOException { MMFUtil.unmap(dataMappedByteBuffer); dataMappedByteBuffer = null; super.close(); } }