/** * Copyright 2011 The Apache Software Foundation * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.hadoop.hbase.index.coprocessor.regionserver; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.index.util.IndexUtils; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Pair; public class IndexRegionScannerForOR implements IndexRegionScanner { private static final Log LOG = LogFactory.getLog(IndexRegionScannerForOR.class); private List<IndexRegionScanner> scanners = null; // private Pair<IndexRegionScanner, KeyValue> lastReturnedValue = null; private byte[] lastReturnedValue = null; // Cache to store the values retrieved by range scans private Map<String, Boolean> rowCache = new HashMap<String, Boolean>(); private Map<byte[], Pair<IndexRegionScanner, KeyValue>> valueMap = new TreeMap<byte[], Pair<IndexRegionScanner, KeyValue>>(Bytes.BYTES_COMPARATOR); private boolean hasRangeScanners = false; private boolean isRootScanner = false; private int scannerIndex = -1; private boolean firstScan = true; private boolean reseeked = false; public IndexRegionScannerForOR(List<IndexRegionScanner> scanners) { this.scanners = scanners; } @Override public void advance() { if (this.lastReturnedValue != null) { // this.lastReturnedValue.getFirst().advance(); } this.lastReturnedValue = null; } @Override public HRegionInfo getRegionInfo() { return null; } @Override public boolean isFilterDone() { return false; } @Override public void setRangeFlag(boolean range) { hasRangeScanners = range; } public void setRootFlag(boolean isRootScanner) { this.isRootScanner = isRootScanner; } @Override public boolean isRange() { return this.hasRangeScanners; } @Override public void setScannerIndex(int index) { scannerIndex = index; } @Override public int getScannerIndex() { return scannerIndex; } @Override public boolean hasChildScanners() { return !scanners.isEmpty(); } @Override public synchronized boolean reseek(byte[] row) throws IOException { boolean success = false; if (!valueMap.isEmpty()) { Iterator<Entry<byte[], Pair<IndexRegionScanner, KeyValue>>> itr = valueMap.entrySet().iterator(); while (itr.hasNext()) { Entry<byte[], Pair<IndexRegionScanner, KeyValue>> entry = itr.next(); IndexRegionScanner scn = entry.getValue().getFirst(); if (Bytes.compareTo(entry.getKey(), row) < 0) { // If the reseek does not retrieve any row then it means we have reached the end of the // scan. So this scanner can be safely removed. if (!scn.reseek(row)) { removeScanner(scn.getScannerIndex()); } itr.remove(); } else { break; } } } reseeked = true; this.lastReturnedValue = null; return success; } private void removeScanner(int scnIndex) { Iterator<IndexRegionScanner> itr = this.scanners.iterator(); while (itr.hasNext()) { if (itr.next().getScannerIndex() == scnIndex) { itr.remove(); break; } } } @Override public synchronized void close() throws IOException { for (IndexRegionScanner scn : this.scanners) { scn.close(); } this.valueMap.clear(); this.scanners.clear(); this.lastReturnedValue = null; } @Override public synchronized boolean next(List<KeyValue> results) throws IOException { OUTER: while (results.size() < 1) { List<KeyValue> intermediateResult = new ArrayList<KeyValue>(); if (hasRangeScanners || firstScan || reseeked) { Iterator<IndexRegionScanner> scnItr = this.scanners.iterator(); INNER: while (scnItr.hasNext()) { IndexRegionScanner scn = scnItr.next(); if (reseeked) { boolean exists = checkForScanner(scn.getScannerIndex()); if (exists) continue; } boolean hasMore = scn.next(intermediateResult); if (!hasRangeScanners) { if (intermediateResult != null && intermediateResult.size() > 0) { byte[] rowKeyFromKV = IndexUtils.getRowKeyFromKV(intermediateResult.get(0)); while (valueMap.containsKey(rowKeyFromKV)) { intermediateResult.clear(); hasMore = scn.next(intermediateResult); if (!intermediateResult.isEmpty()) { rowKeyFromKV = IndexUtils.getRowKeyFromKV(intermediateResult.get(0)); } else { break; } } if (!hasMore && intermediateResult.isEmpty()) { // Allow other scanners to scan. Nothing to do. } else { valueMap.put(rowKeyFromKV, new Pair<IndexRegionScanner, KeyValue>(scn, intermediateResult.get(0))); intermediateResult.clear(); } } } if (!hasMore) { if (LOG.isDebugEnabled()) { LOG.debug("Removing scanner " + scn + " from the list."); } scn.close(); scnItr.remove(); } if (hasRangeScanners) { if (!intermediateResult.isEmpty()) { String rowKey = Bytes.toString(IndexUtils.getRowKeyFromKV(intermediateResult.get(0))); if (isRootScanner && !rowCache.containsKey(rowKey)) { rowCache.put(rowKey, false); results.addAll(intermediateResult); return !this.scanners.isEmpty(); } else if (isRootScanner) { // dont add to results because already exists and scan for other entry. intermediateResult.clear(); continue OUTER; } else { results.addAll(intermediateResult); return !this.scanners.isEmpty(); } } } } if (firstScan) firstScan = false; if (reseeked) reseeked = false; } else { // Scan on previous scanner which returned minimum values. Entry<byte[], Pair<IndexRegionScanner, KeyValue>> minEntry = null; if (!valueMap.isEmpty()) { minEntry = valueMap.entrySet().iterator().next(); IndexRegionScanner scn = minEntry.getValue().getFirst(); if (Bytes.compareTo(lastReturnedValue, minEntry.getKey()) == 0) { valueMap.remove(minEntry.getKey()); boolean hasMore = scn.next(intermediateResult); byte[] rowKeyFromKV = null; if (!intermediateResult.isEmpty()) { rowKeyFromKV = IndexUtils.getRowKeyFromKV(intermediateResult.get(0)); while (valueMap.containsKey(rowKeyFromKV)) { intermediateResult.clear(); if (hasMore) { hasMore = minEntry.getValue().getFirst().next(intermediateResult); } if (intermediateResult.isEmpty()) { rowKeyFromKV = null; scn.close(); removeScanner(scn.getScannerIndex()); break; } rowKeyFromKV = IndexUtils.getRowKeyFromKV(intermediateResult.get(0)); } } if (rowKeyFromKV != null) { valueMap.put(rowKeyFromKV, new Pair<IndexRegionScanner, KeyValue>(scn, intermediateResult.get(0))); intermediateResult.clear(); } } } else { return false; } } if (!valueMap.isEmpty()) { Entry<byte[], Pair<IndexRegionScanner, KeyValue>> minEntry = valueMap.entrySet().iterator().next(); lastReturnedValue = minEntry.getKey(); results.add(minEntry.getValue().getSecond()); return true; } else { return false; } } if (LOG.isDebugEnabled()) { LOG.debug(results.size() + " seek points obtained. Values: " + (!results.isEmpty() ? Bytes.toString(results.get(0).getRow()) : 0)); } return !results.isEmpty(); } private boolean checkForScanner(int scnIndex) { Collection<Pair<IndexRegionScanner, KeyValue>> scanners = valueMap.values(); for (Pair<IndexRegionScanner, KeyValue> scn : scanners) { if (scnIndex == scn.getFirst().getScannerIndex()) { return true; } } return false; } /* * private void getTheSmallerValue(KeyValue keyValue, IndexRegionScanner scn ) { byte[] currentRow * = getRowKeyFromKV(keyValue); if(this.lastReturnedValue == null){ * this.lastReturnedValue.setFirst(new Pair<byte[], Integer>(currentRow,scn.getScannerIndex())); * this.lastReturnedValue.setSecond(keyValue); } else { byte[] lastRow = * getRowKeyFromKV(this.lastReturnedValue.getSecond()); if(Bytes.compareTo(currentRow, lastRow) < * 0){ this.lastReturnedValue.setFirst(new Pair<byte[], * Integer>(currentRow,scn.getScannerIndex())); this.lastReturnedValue.setSecond(keyValue); } // * TODO: In case of equal need to check what to do? if(Bytes.compareTo(currentRow, lastRow) == 0){ * scn.advance(); } } } */ @Override public boolean next(List<KeyValue> result, int limit) throws IOException { // TODO Auto-generated method stub return false; } @Override public long getMvccReadPoint() { // TODO Implement RegionScanner.getMvccReadPoint return 0; } @Override public boolean nextRaw(List<KeyValue> result, String metric) throws IOException { // TODO Implement RegionScanner.nextRaw return false; } @Override public boolean nextRaw(List<KeyValue> result, int limit, String metric) throws IOException { // TODO Implement RegionScanner.nextRaw return false; } @Override public boolean next(List<KeyValue> results, String metric) throws IOException { // TODO Implement InternalScanner.next return false; } @Override public boolean next(List<KeyValue> result, int limit, String metric) throws IOException { // TODO Implement InternalScanner.next return false; } }