package org.wonderdb.core.collection.impl; /******************************************************************************* * Copyright 2013 Vilas Athavale * * 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. *******************************************************************************/ import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.Stack; import java.util.concurrent.atomic.AtomicLong; import org.jboss.netty.buffer.ChannelBuffer; import org.wonderdb.block.Block; import org.wonderdb.block.BlockEntryPosition; import org.wonderdb.block.BlockManager; import org.wonderdb.block.IndexBlock; import org.wonderdb.block.IndexCompareIndexQuery; import org.wonderdb.block.IndexLeafBlock; import org.wonderdb.block.IndexQuery; import org.wonderdb.cache.impl.CacheEntryPinner; import org.wonderdb.cache.impl.CacheHandler; import org.wonderdb.cache.impl.SecondaryCacheHandlerFactory; import org.wonderdb.core.collection.BTree; import org.wonderdb.core.collection.RecordUtils; import org.wonderdb.core.collection.ResultIterator; import org.wonderdb.file.StorageUtils; import org.wonderdb.seralizers.block.SerializedBlockImpl; import org.wonderdb.serialize.ColumnSerializer; import org.wonderdb.serialize.DefaultSerializer; import org.wonderdb.serialize.SerializerManager; import org.wonderdb.serialize.block.BlockSerilizer; import org.wonderdb.serialize.record.RecordSerializer; import org.wonderdb.txnlogger.LogManager; import org.wonderdb.txnlogger.TransactionId; import org.wonderdb.types.BlockPtr; import org.wonderdb.types.BlockPtrList; import org.wonderdb.types.ColumnSerializerMetadata; import org.wonderdb.types.DBType; import org.wonderdb.types.Extended; import org.wonderdb.types.ExtendedColumn; import org.wonderdb.types.IndexKeyType; import org.wonderdb.types.TypeMetadata; import org.wonderdb.types.record.IndexRecord; import org.wonderdb.types.record.ObjectRecord; public class HashIndexImpl implements BTree { private static CacheHandler<BlockPtr, ChannelBuffer> secondaryCacheHandler = SecondaryCacheHandlerFactory.getInstance().getCacheHandler(); private static TypeMetadata BUCKET_META = new ColumnSerializerMetadata(SerializerManager.BLOCK_PTR); private static int BUCKETS_PER_BLOCK = 150; TypeMetadata meta; BlockPtr head = null; List<BlockPtr> buckets = null; int allBuckets = 0; private AtomicLong links = new AtomicLong(0); private HashIndexImpl(BlockPtr head, TypeMetadata meta) { if (head != null && head.getFileId() >= 0 && head.getBlockPosn() >= 0) { this.head = head; } this.meta = meta; } public static BTree create(boolean unique, BlockPtr head, int bucketSize, TypeMetadata indexMeta, TransactionId txnId, Set<Object> pinnedBlocks) { HashIndexImpl tree = new HashIndexImpl(head, indexMeta); IndexLeafBlock headBlock = (IndexLeafBlock) BlockManager.getInstance().createIndexLefBlock(head, pinnedBlocks); List<BlockPtr> list = new ArrayList<BlockPtr>(bucketSize); list.add(head); IndexLeafBlock currentBlock = headBlock; Set<Object> pinnedBs = new HashSet<Object>(); for (int i = 1; i < bucketSize; i++) { IndexLeafBlock block = (IndexLeafBlock) BlockManager.getInstance().createIndexBlock(head.getFileId(), pinnedBs); currentBlock.setNext(block.getPtr()); tree.initList(currentBlock); BlockSerilizer.getInstance().serialize(currentBlock, BUCKET_META, txnId); currentBlock = block; list.add(block.getPtr()); CacheEntryPinner.getInstance().unpin(block.getPtr(), pinnedBs); } tree.initList(currentBlock); BlockSerilizer.getInstance().serialize(currentBlock, BUCKET_META, txnId); tree.buckets = list; tree.allBuckets = list.size()*BUCKETS_PER_BLOCK; return tree; } public static HashIndexImpl load(BlockPtr head, TypeMetadata meta, Set<Object> pinnedBlocks) { Set<Object> localPinnedBlocks = new HashSet<Object>(); IndexLeafBlock headBlock = (IndexLeafBlock) BlockManager.getInstance().getBlock(head, BUCKET_META, localPinnedBlocks); List<BlockPtr> list = new ArrayList<BlockPtr>(); Block currentBlock = headBlock; while (currentBlock != null) { BlockPtr ptr = currentBlock.getPtr(); list.add(ptr); currentBlock = BlockManager.getInstance().getBlock(currentBlock.getNext(), BUCKET_META, localPinnedBlocks); CacheEntryPinner.getInstance().unpin(localPinnedBlocks, localPinnedBlocks); } HashIndexImpl hii = new HashIndexImpl(head, meta); hii.buckets = list; hii.allBuckets = list.size()*BUCKETS_PER_BLOCK; return hii; } @Override public ResultIterator getHead(boolean writeLock, Set<Object> pinnedBlocks) { throw new RuntimeException("Method not supported"); } @Override public IndexKeyType remove(IndexKeyType entry, Set<Object> pinnedBocks, TransactionId txnId) { ObjectRecord record = null; record = (ObjectRecord) removeInternal(entry, pinnedBocks, false, txnId); if (record != null) { DBType column = record.getColumn(); if (column instanceof ExtendedColumn) { return (IndexKeyType) ((ExtendedColumn) column).getValue(meta); } return (IndexKeyType) column; } return null; } private ObjectRecord removeInternal(IndexKeyType entry, Set<Object> pinnedBlocks, boolean readLock, TransactionId txnId) { // Set<Object> findPinnedBlocks = new HashSet<Object>(); IndexBlock changedBlock = null; Set<Block> cBlocks = new HashSet<Block>(); BTreeIteratorImpl iter = null; ObjectRecord data = null; Stack<BlockPtr> callBlockStack = new Stack<BlockPtr>(); if (entry == null) { return null; } try { int hashCode = Math.abs(entry.hashCode()); int posn = hashCode % buckets.size(); BlockPtr ptr = buckets.get(posn); Block block = BlockManager.getInstance().getBlock(ptr, BUCKET_META, pinnedBlocks); IndexBlock indexBlock = null; BlockPtr ptrInBucket = null; block.readLock(); try { IndexRecord record = (IndexRecord) block.getData().get(0); BlockPtrList list = (BlockPtrList) record.getColumn(); int p = hashCode % BUCKETS_PER_BLOCK; if (list.getPtrList().size() <= p || list.getPtrList().get(p) == null || list.getPtrList().get(p).getBlockPosn() < 0) { return null; } else { ptrInBucket = list.getPtrList().get(p); } } finally { block.readUnlock(); } indexBlock = (IndexBlock) BlockManager.getInstance().getBlock(ptrInBucket, meta, pinnedBlocks); boolean removed = false; BlockEntryPosition bep = null; IndexCompareIndexQuery query = new IndexCompareIndexQuery(entry.getKey(), true, meta, pinnedBlocks); while (indexBlock != null) { try { bep = indexBlock.find(query, true, pinnedBlocks, callBlockStack); iter = new BTreeIteratorImpl(bep, this, true, cBlocks, readLock, meta); if (iter.hasNext()) { data = (ObjectRecord) iter.next(); if (data.getColumn().compareTo(entry.getKey()) == 0) { iter.remove(); removed = true; Iterator<Block> cBlocksIter = cBlocks.iterator(); while (cBlocksIter.hasNext()) { changedBlock = (IndexBlock) cBlocksIter.next(); break; } BlockSerilizer.getInstance().serialize(changedBlock, meta, txnId); break; } else { indexBlock = (IndexBlock) BlockManager.getInstance().getBlock(indexBlock.getNext(), meta, pinnedBlocks); } } } finally { iter.unlock(); } } if (removed) { if (data instanceof Extended) { RecordUtils.getInstance().releaseRecord(data); } } } finally { } return data; } private void initList(Block block) { for (int i = 0; i < BUCKETS_PER_BLOCK; i++) { IndexRecord record = new IndexRecord(); record.setColumn(DefaultSerializer.NULL_BLKPTR); block.getData().add(record); } } @Override public ResultIterator find(IndexQuery entry, boolean writeLock, Set<Object> pBlocks) { Set<Object> pinnedBlocks = pBlocks; ResultIterator iter = null; IndexCompareIndexQuery query = null; if (entry == null ) { return EmptyResultIterator.getInstance(); } if (entry instanceof IndexCompareIndexQuery) { query = (IndexCompareIndexQuery) entry; } else { throw new RuntimeException("Index Query type not supported" + entry); } BlockEntryPosition bep = null; Set<Block> cBlocks = new HashSet<Block>(); ObjectRecord data = null; IndexBlock bucketBlock = null; try { int hashCode = Math.abs(query.hashCode()); int posn = hashCode % allBuckets; int bucketPosn = posn % buckets.size(); posn = posn / buckets.size(); BlockPtr p = buckets.get(bucketPosn); bucketBlock = (IndexBlock) BlockManager.getInstance().getBlock(p, BUCKET_META, pinnedBlocks); bucketBlock.readLock(); try { IndexRecord record = (IndexRecord) bucketBlock.getData().get(posn); // BlockPtrList list = (BlockPtrList) record.getColumn(); p = (BlockPtr) record.getColumn(); // int x = hashCode >> 15; if (p == null || p.getFileId() < 0) { return EmptyResultIterator.getInstance(); // return null; } } finally { bucketBlock.readUnlock(); } Stack<BlockPtr> callBlockStack = new Stack<BlockPtr>(); boolean found = false; bucketBlock = (IndexBlock) BlockManager.getInstance().getBlock(p, meta, pinnedBlocks); while (bucketBlock != null) { try { bep = bucketBlock.find(query, writeLock, pinnedBlocks, callBlockStack); iter = new BTreeIteratorImpl(bep, this, writeLock, cBlocks, !writeLock, meta); data = (ObjectRecord) iter.peek(); if (data != null && entry.compareTo(data.getColumn()) == 0) { found = true; return iter; } else { bucketBlock = (IndexBlock) BlockManager.getInstance().getBlock(bucketBlock.getNext(), meta, pinnedBlocks); } } finally { if (!found && iter != null) { iter.unlock(); } } } } finally { // CacheEntryPinner.getInstance().unpin(pinnedBlocks, pinnedBlocks); } return EmptyResultIterator.getInstance(); } @Override public ResultIterator iterator() { throw new RuntimeException("Method not supported"); } @Override public void insert(IndexKeyType data, Set<Object> pinnedBlocks, TransactionId txnId) { insertInternal(data, pinnedBlocks, false, txnId); } private void insertInternal(IndexKeyType data, Set<Object> pinnedBlocks, boolean readLock, TransactionId txnId) { BlockEntryPosition bep = null; Set<Block> changedBlocks = new HashSet<Block>(); int hashCode = Math.abs(data.hashCode()); int posn = hashCode % allBuckets; int bucketPosn = posn % buckets.size(); posn = posn / buckets.size(); BlockPtr p = buckets.get(bucketPosn); boolean needWriteLock = false; IndexBlock indexBlock = null; IndexBlock bucketBlock = (IndexBlock) BlockManager.getInstance().getBlock(p, BUCKET_META, pinnedBlocks); bucketBlock.readLock(); try { // BlockPtrList list = null; IndexRecord record = (IndexRecord) bucketBlock.getData().get(posn); // list = (BlockPtrList) record.getColumn(); // int x = hashCode >> 15; BlockPtr p1 =((BlockPtr)record.getColumn()); if (p1 == null || p1.getFileId() < 0) { needWriteLock = true; } else { indexBlock = (IndexBlock) BlockManager.getInstance().getBlock(p1, meta, pinnedBlocks); } } finally { bucketBlock.readUnlock(); } if (needWriteLock) { bucketBlock.writeLock(); try { // BlockPtrList list = null; IndexRecord record = (IndexRecord) bucketBlock.getData().get(posn); // list = (BlockPtrList) record.getColumn(); BlockPtr p1 =((BlockPtr)record.getColumn()); if (p1 == null || p1.getFileId() < 0) { indexBlock = (IndexLeafBlock) BlockManager.getInstance().createIndexBlock(head.getFileId(), pinnedBlocks); record.setColumn(indexBlock.getPtr()); BlockSerilizer.getInstance().serialize(bucketBlock, BUCKET_META, txnId); // BlockSerilizer.getInstance().serialize(indexBlock, meta, txnId); } else { indexBlock = (IndexBlock) BlockManager.getInstance().getBlock(p1, meta, pinnedBlocks); } } finally { bucketBlock.writeUnlock(); } } IndexCompareIndexQuery query = new IndexCompareIndexQuery(data, true, meta, pinnedBlocks); ResultIterator iter = null; IndexRecord record = null; IndexBlock prevBlock = null; IndexBlock firstBlock = null; try { // if (p == null || p.getBlockPosn() < 0) { // changedBlocks.add(indexBlock); // } int recordSize = -1; try { // iter = find(query, true, pinnedBlocks); // if (iter != null && iter != EmptyResultIterator.getInstance()) { // throw new UniqueKeyViolationException(); // } record = new IndexRecord(); record.setColumn(data); int maxBlockSize = StorageUtils.getInstance().getTotalBlockSize(indexBlock.getPtr()); recordSize = RecordSerializer.getInstance().getRecordSize(record, meta); int maxSize = maxBlockSize - BlockSerilizer.INDEX_BLOCK_HEADER - SerializedBlockImpl.HEADER_SIZE; if (recordSize >= maxSize) { record = (IndexRecord) RecordUtils.getInstance().convertToExtended(record, pinnedBlocks, meta, maxSize, head.getFileId()); recordSize = RecordSerializer.getInstance().getRecordSize(record, meta); } firstBlock = indexBlock; prevBlock = indexBlock; firstBlock.writeLock(); int count = 0; while (indexBlock != null) { prevBlock = indexBlock; int blockSize = BlockSerilizer.getInstance().getBlockSize(indexBlock, meta); if ((blockSize + recordSize) >= maxSize) { indexBlock = (IndexBlock) BlockManager.getInstance().getBlock(indexBlock.getNext(), meta, pinnedBlocks); count++; } else { break; } } if (indexBlock == null) { indexBlock = (IndexBlock) BlockManager.getInstance().createIndexBlock(head.getFileId(), pinnedBlocks); count++; prevBlock.setNext(indexBlock.getPtr()); changedBlocks.add(prevBlock); } boolean next = links.compareAndSet((count-1), count); if (next) { System.out.println(count); } Stack<BlockPtr> blockStack = new Stack<BlockPtr>(); bep = indexBlock.find(query, true, pinnedBlocks, blockStack); if (bep.getPosn() >= indexBlock.getData().size()) { indexBlock.getData().add(record); } else { indexBlock.getData().add(bep.getPosn(), record); } changedBlocks.add(indexBlock); Iterator<Block> iter1 = changedBlocks.iterator(); while(iter1.hasNext()) { Block b = iter1.next(); BlockSerilizer.getInstance().serialize(b, meta, txnId); } if (record.getColumn() instanceof Extended) { maxBlockSize = StorageUtils.getInstance().getTotalBlockSize(indexBlock.getPtr()) - BlockSerilizer.INDEX_BLOCK_HEADER - SerializedBlockImpl.HEADER_SIZE; ColumnSerializer.getInstance().serializeExtended(indexBlock.getPtr().getFileId(), (ExtendedColumn) record.getColumn(), maxBlockSize, meta, pinnedBlocks); List<BlockPtr> list1 = ((Extended)record.getColumn()).getPtrList(); for (int i = 0; i < list1.size(); i++) { BlockPtr p1 = list1.get(i); CacheEntryPinner.getInstance().pin(p1, pinnedBlocks); SerializedBlockImpl serializedBlock = (SerializedBlockImpl) secondaryCacheHandler.get(p); secondaryCacheHandler.changed(p); LogManager.getInstance().logBlock(txnId, serializedBlock); } } } finally { indexBlock.writeUnlock(); } } finally { firstBlock.writeUnlock(); if (readLock) { readUnlock(); } else { writeUnlock(); } } } public void readLock() { } public void readUnlock() { } public void writeLock() { } public void writeUnlock() { } public BlockPtr getHeadPtr() { return head; } public BlockPtr getRootPtr() { return null; } }