From 688cbfb776130b60f64a65bb95e8b78c0fce38c7 Mon Sep 17 00:00:00 2001 From: orbiter Date: Mon, 16 Oct 2006 00:27:25 +0000 Subject: [PATCH] - bugfixing for flextable bug - bugfixing for collection index bug - several other bugfixes git-svn-id: https://svn.berlios.de/svnroot/repos/yacy/trunk@2785 6c8d7289-2bf4-0310-a012-ef5d649a1542 --- doc/Download.html | 4 +- htroot/IndexControl_p.html | 4 +- source/de/anomic/http/httpdFileHandler.java | 1 - .../anomic/kelondro/kelondroBytesIntMap.java | 2 + .../kelondro/kelondroCollectionIndex.java | 42 ++-- .../de/anomic/kelondro/kelondroFlexTable.java | 11 +- .../anomic/kelondro/kelondroIntBytesMap.java | 19 +- .../de/anomic/kelondro/kelondroRecords.java | 5 +- .../kelondro/kelondroRowBufferedSet.java | 214 ++++++++---------- .../kelondro/kelondroRowCollection.java | 4 - source/de/anomic/kelondro/kelondroRowSet.java | 34 +-- 11 files changed, 141 insertions(+), 199 deletions(-) diff --git a/doc/Download.html b/doc/Download.html index 1e374f672..81c53de84 100644 --- a/doc/Download.html +++ b/doc/Download.html @@ -64,8 +64,8 @@ Nightly builds from compiles out of SVN can be obtained from yacy_v0.47_20060927_2665.exe -
  • from BerliOS.de : yacy_v0.47_20060927_2665.exe
  • +
  • from yacy.net   : yacy_v0.48_20061010_2743.exe
  • +
  • from BerliOS.de : yacy_v0.48_20061010_2743.exe
  • diff --git a/htroot/IndexControl_p.html b/htroot/IndexControl_p.html index 06a935d42..ada516dc4 100644 --- a/htroot/IndexControl_p.html +++ b/htroot/IndexControl_p.html @@ -128,8 +128,8 @@ URL entries related to this word hash #[keyHash]#

    #{urlList}# #(urlExists)# - #[urlhxValue]# <unresolved URL Hash>
    - :: + #[urlhxValue]# <unresolved URL Hash>
    + :: #[urlhxValue]# #[urlString]#, pos=#[pos]#
    #(/urlExists)# #{/urlList}# diff --git a/source/de/anomic/http/httpdFileHandler.java b/source/de/anomic/http/httpdFileHandler.java index 9452919cc..485b37349 100644 --- a/source/de/anomic/http/httpdFileHandler.java +++ b/source/de/anomic/http/httpdFileHandler.java @@ -91,7 +91,6 @@ import java.lang.ref.SoftReference; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URLDecoder; -import java.nio.Buffer; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Date; diff --git a/source/de/anomic/kelondro/kelondroBytesIntMap.java b/source/de/anomic/kelondro/kelondroBytesIntMap.java index c476ca2e8..f5894550e 100644 --- a/source/de/anomic/kelondro/kelondroBytesIntMap.java +++ b/source/de/anomic/kelondro/kelondroBytesIntMap.java @@ -53,6 +53,8 @@ public class kelondroBytesIntMap { } public synchronized int removei(byte[] key) throws IOException { + // returns the integer index of the key, if the key can be found and was removed + // and -1 if the key was not found. if (ki.size() == 0) return -1; kelondroRow.Entry indexentry = ki.remove(key); if (indexentry == null) return -1; diff --git a/source/de/anomic/kelondro/kelondroCollectionIndex.java b/source/de/anomic/kelondro/kelondroCollectionIndex.java index fbeb1f7cd..ca03e9135 100644 --- a/source/de/anomic/kelondro/kelondroCollectionIndex.java +++ b/source/de/anomic/kelondro/kelondroCollectionIndex.java @@ -105,7 +105,7 @@ public class kelondroCollectionIndex { // open array files this.arrays = new HashMap(); // all entries will be dynamically created with getArray() if (((fileIndexGeneration) || (ramIndexGeneration))) { - serverLog.logFine("STARTUP", "STARTED MIGRATION OF OLD COLLECION INDEX TO NEW COLLECTION INDEX. THIS WILL TAKE SOME TIME"); + serverLog.logFine("STARTUP", "STARTED INITIALIZATION OF NEW COLLECTION INDEX. THIS WILL TAKE SOME TIME"); openAllArrayFiles(((fileIndexGeneration) || (ramIndexGeneration)), indexOrder); } @@ -265,38 +265,39 @@ public class kelondroCollectionIndex { synchronized (index) { // first find an old entry, if one exists - kelondroRow.Entry oldindexrow = index.get(key); + kelondroRow.Entry indexrow = index.get(key); - if (oldindexrow == null) { + if (indexrow == null) { if ((collection != null) && (collection.size() > 0)) { // the collection is new - overwrite(key, collection, arrayIndex(collection.size())); + overwrite(key, collection, arrayIndex(collection.size()), index.row().newEntry()); } return 0; } // overwrite the old collection // read old information - int oldchunksize = (int) oldindexrow.getColLong(idx_col_chunksize); // needed only for migration - int oldchunkcount = (int) oldindexrow.getColLong(idx_col_chunkcount); - int oldrownumber = (int) oldindexrow.getColLong(idx_col_indexpos); - int oldPartitionNumber = (int) oldindexrow.getColByte(idx_col_clusteridx); + int oldchunksize = (int) indexrow.getColLong(idx_col_chunksize); // needed only for migration + int oldchunkcount = (int) indexrow.getColLong(idx_col_chunkcount); + int oldrownumber = (int) indexrow.getColLong(idx_col_indexpos); + int oldPartitionNumber = (int) indexrow.getColByte(idx_col_clusteridx); assert (oldPartitionNumber >= arrayIndex(oldchunkcount)); int oldSerialNumber = 0; if (merge) { // load the old collection and join it - kelondroRowSet oldcollection = getwithparams(oldindexrow, oldchunksize, oldchunkcount, oldPartitionNumber, oldrownumber, oldSerialNumber, false, false); + kelondroRowSet oldcollection = getwithparams(indexrow, oldchunksize, oldchunkcount, oldPartitionNumber, oldrownumber, oldSerialNumber, false, false); // join with new collection oldcollection.addAll(collection); + oldcollection.shape(); collection = oldcollection; } int removed = 0; if (removekeys != null) { // load the old collection and remove keys - kelondroRowSet oldcollection = getwithparams(oldindexrow, oldchunksize, oldchunkcount, oldPartitionNumber, oldrownumber, oldSerialNumber, false, false); + kelondroRowSet oldcollection = getwithparams(indexrow, oldchunksize, oldchunkcount, oldPartitionNumber, oldrownumber, oldSerialNumber, false, false); // remove the keys from the set Iterator i = removekeys.iterator(); @@ -314,6 +315,12 @@ public class kelondroCollectionIndex { if (deletecomplete) { kelondroFixedWidthArray array = getArray(oldPartitionNumber, oldSerialNumber, oldchunksize); array.remove(oldrownumber); + index.remove(key); + } else { + // update the index entry + indexrow.setCol(idx_col_chunkcount, 0); + indexrow.setCol(idx_col_lastwrote, kelondroRowCollection.daysSince2000(System.currentTimeMillis())); + index.put(indexrow); } return removed; } @@ -337,11 +344,11 @@ public class kelondroCollectionIndex { array.set(oldrownumber, arrayEntry); // update the index entry - oldindexrow.setCol(idx_col_chunkcount, collection.size()); - oldindexrow.setCol(idx_col_clusteridx, (byte) oldPartitionNumber); - oldindexrow.setCol(idx_col_flags, (byte) 0); - oldindexrow.setCol(idx_col_lastwrote, kelondroRowCollection.daysSince2000(System.currentTimeMillis())); - index.put(oldindexrow); + indexrow.setCol(idx_col_chunkcount, collection.size()); + indexrow.setCol(idx_col_clusteridx, (byte) oldPartitionNumber); + indexrow.setCol(idx_col_flags, (byte) 0); + indexrow.setCol(idx_col_lastwrote, kelondroRowCollection.daysSince2000(System.currentTimeMillis())); + index.put(indexrow); } else { // we need a new slot, that means we must first delete the old entry // find array file @@ -351,13 +358,13 @@ public class kelondroCollectionIndex { array.remove(oldrownumber); // write a new entry in the other array - overwrite(key, collection, newPartitionNumber); + overwrite(key, collection, newPartitionNumber, indexrow); } return removed; } } - private void overwrite(byte[] key, kelondroRowCollection collection, int targetpartition) throws IOException { + private void overwrite(byte[] key, kelondroRowCollection collection, int targetpartition, kelondroRow.Entry indexEntry) throws IOException { // helper method, should not be called directly and only within a synchronized(index) environment // simply store a collection without check if the collection existed before @@ -373,7 +380,6 @@ public class kelondroCollectionIndex { int newRowNumber = array.add(arrayEntry); // store the new row number in the index - kelondroRow.Entry indexEntry = index.row().newEntry(); indexEntry.setCol(idx_col_key, key); indexEntry.setCol(idx_col_chunksize, this.playloadrow.objectsize()); indexEntry.setCol(idx_col_chunkcount, collection.size()); diff --git a/source/de/anomic/kelondro/kelondroFlexTable.java b/source/de/anomic/kelondro/kelondroFlexTable.java index e5bc41edb..18ca3b2aa 100644 --- a/source/de/anomic/kelondro/kelondroFlexTable.java +++ b/source/de/anomic/kelondro/kelondroFlexTable.java @@ -80,8 +80,9 @@ public class kelondroFlexTable extends kelondroFlexWidthArray implements kelondr } private kelondroIndex initializeRamIndex(kelondroOrder objectOrder) throws IOException { - kelondroRowBufferedSet ri = new kelondroRowBufferedSet(new kelondroRow(new kelondroColumn[]{super.row().column(0), new kelondroColumn("int c-4 {b256}")}), 0); - ri.setOrdering(objectOrder, 0); + kelondroRowBufferedSet ri = new kelondroRowBufferedSet(new kelondroRow(new kelondroColumn[]{super.row().column(0), new kelondroColumn("int c-4 {b256}")}), objectOrder, 0, 0); + //kelondroRowSet ri = new kelondroRowSet(new kelondroRow(new kelondroColumn[]{super.row().column(0), new kelondroColumn("int c-4 {b256}")}), 0); + //ri.setOrdering(objectOrder, 0); Iterator content = super.col[0].contentNodes(-1); kelondroRecords.Node node; kelondroRow.Entry indexentry; @@ -89,10 +90,10 @@ public class kelondroFlexTable extends kelondroFlexWidthArray implements kelondr while (content.hasNext()) { node = (kelondroRecords.Node) content.next(); i = node.handle().hashCode(); - indexentry = ri.rowdef.newEntry(); + indexentry = ri.row().newEntry(); indexentry.setCol(0, node.getValueRow()); indexentry.setCol(1, i); - ri.add(indexentry); + ri.put(indexentry); if ((i % 10000) == 0) { System.out.print('.'); System.out.flush(); @@ -100,7 +101,7 @@ public class kelondroFlexTable extends kelondroFlexWidthArray implements kelondr } System.out.print(" -ordering- "); System.out.flush(); - ri.shape(); + ri.trim(); return ri; } diff --git a/source/de/anomic/kelondro/kelondroIntBytesMap.java b/source/de/anomic/kelondro/kelondroIntBytesMap.java index 0f9e42ab3..e6250422b 100644 --- a/source/de/anomic/kelondro/kelondroIntBytesMap.java +++ b/source/de/anomic/kelondro/kelondroIntBytesMap.java @@ -29,10 +29,7 @@ package de.anomic.kelondro; public class kelondroIntBytesMap extends kelondroRowBufferedSet { public kelondroIntBytesMap(int payloadSize, int initSize) { - super(new kelondroRow("Cardinal key-4 {b256}, byte[] payload-" + payloadSize), initSize); - - // initialize ordering - super.setOrdering(kelondroNaturalOrder.naturalOrder, 0); + super(new kelondroRow("Cardinal key-4 {b256}, byte[] payload-" + payloadSize), kelondroNaturalOrder.naturalOrder, 0, initSize); } public byte[] getb(int ii) { @@ -42,27 +39,19 @@ public class kelondroIntBytesMap extends kelondroRowBufferedSet { } public byte[] putb(int ii, byte[] value) { - kelondroRow.Entry newentry = rowdef.newEntry(); + kelondroRow.Entry newentry = super.row().newEntry(); newentry.setCol(0, (long) ii); newentry.setCol(1, value); kelondroRow.Entry oldentry = super.put(newentry); if (oldentry == null) return null; return oldentry.getColBytes(1); } - - public void addb(int ii, byte[] value) { - kelondroRow.Entry newentry = rowdef.newEntry(); - newentry.setCol(0, (long) ii); - newentry.setCol(1, value); - add(newentry); - } - + public byte[] removeb(int ii) { if (size() == 0) { - if (System.currentTimeMillis() - this.lastTimeWrote > 10000) this.trim(); return null; } - kelondroRow.Entry indexentry = super.removeMarked(kelondroNaturalOrder.encodeLong((long) ii, 4)); + kelondroRow.Entry indexentry = super.remove(kelondroNaturalOrder.encodeLong((long) ii, 4)); if (indexentry == null) return null; return indexentry.getColBytes(1); } diff --git a/source/de/anomic/kelondro/kelondroRecords.java b/source/de/anomic/kelondro/kelondroRecords.java index 34c184d0d..2e9f4e72f 100644 --- a/source/de/anomic/kelondro/kelondroRecords.java +++ b/source/de/anomic/kelondro/kelondroRecords.java @@ -416,7 +416,6 @@ public class kelondroRecords { } else { this.cacheSize = (int) (buffersize / cacheNodeChunkSize()); this.cacheHeaders = new kelondroIntBytesMap(this.headchunksize, 0); - this.cacheHeaders.setOrdering(kelondroNaturalOrder.naturalOrder, 0); } this.readHit = 0; this.readMiss = 0; @@ -434,10 +433,10 @@ public class kelondroRecords { Node n; while ((System.currentTimeMillis() < stop) && (cacheHeaders.size() < cacheSize) && (i.hasNext())) { n = (Node) i.next(); - cacheHeaders.addb(n.handle.index, n.headChunk); + cacheHeaders.putb(n.handle.index, n.headChunk); count++; } - cacheHeaders.shape(); + cacheHeaders.trim(); logFine("preloaded " + count + " records into cache"); } catch (kelondroException e) { // the contentNodes iterator had a time-out; we don't do a preload diff --git a/source/de/anomic/kelondro/kelondroRowBufferedSet.java b/source/de/anomic/kelondro/kelondroRowBufferedSet.java index 0aec488be..84345c13f 100644 --- a/source/de/anomic/kelondro/kelondroRowBufferedSet.java +++ b/source/de/anomic/kelondro/kelondroRowBufferedSet.java @@ -24,6 +24,7 @@ package de.anomic.kelondro; +import java.util.Date; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; @@ -31,24 +32,20 @@ import java.util.TreeMap; import de.anomic.server.serverMemory; -public class kelondroRowBufferedSet extends kelondroRowSet { +public class kelondroRowBufferedSet implements kelondroIndex { - private static final long memBlockLimit = 2000000; // do not fill cache further if the amount of available memory is less that this + private static final long memBlockLimit = 2000000; // do not fill cache further if the amount of available memory is less that this private static final int bufferFlushLimit = 10000; - private static final int bufferFlushMinimum = 1000; - private final boolean useRowCollection = true; + private static final int bufferFlushMinimum = 1000; private kelondroProfile profile; private TreeMap buffer; + private kelondroRowSet store; - public kelondroRowBufferedSet(kelondroRow rowdef) { - super(rowdef); - buffer = new TreeMap(kelondroNaturalOrder.naturalOrder); - profile = new kelondroProfile(); - } - - public kelondroRowBufferedSet(kelondroRow rowdef, int objectCount) { - super(rowdef, objectCount); - buffer = new TreeMap(kelondroNaturalOrder.naturalOrder); + public kelondroRowBufferedSet(kelondroRow rowdef, kelondroOrder objectOrder, int orderColumn, int objectCount) { + store = new kelondroRowSet(rowdef, objectCount); + assert (objectOrder != null); + store.setOrdering(objectOrder, orderColumn); + buffer = new TreeMap(objectOrder); profile = new kelondroProfile(); } @@ -58,145 +55,126 @@ public class kelondroRowBufferedSet extends kelondroRowSet { Map.Entry entry; while (i.hasNext()) { entry = (Map.Entry) i.next(); - super.add((kelondroRow.Entry) entry.getValue()); + store.add((kelondroRow.Entry) entry.getValue()); } buffer.clear(); } - public final void trim() { - synchronized (buffer) { - flush(); - super.trim(); - } + public synchronized final void trim() { + flush(); + store.trim(); } - - public void removeOne() { - synchronized (buffer) { - if (buffer.size() == 0) { - super.removeOne(); - } else try { - //buffer.remove(buffer.keySet().iterator().next()); - buffer.remove(buffer.lastKey()); - } catch (NoSuchElementException e) {} - } + + public synchronized void removeOne() { + if (buffer.size() == 0) { + store.removeOne(); + } else try { + // buffer.remove(buffer.keySet().iterator().next()); + buffer.remove(buffer.lastKey()); + } catch (NoSuchElementException e) {} } - - public void clear() { - synchronized (buffer) { - super.clear(); - buffer.clear(); - } + + public synchronized void clear() { + store.clear(); + buffer.clear(); } - - public int size() { - synchronized (buffer) { - return buffer.size() + super.size(); - } + + public synchronized int size() { + return buffer.size() + store.size(); } - - public Iterator rows() { - synchronized (buffer) { - flush(); - } - return super.rows(); + + public synchronized Iterator rows() { + flush(); + return store.rows(); } - - public void uniq() { - synchronized (buffer) { - flush(); - super.uniq(); - } + + public synchronized void uniq() { + flush(); + store.uniq(); } - - public String toString() { - synchronized (buffer) { - flush(); - return super.toString(); - } + + public synchronized String toString() { + flush(); + return store.toString(); } - - public kelondroRow.Entry get(byte[] key) { + + public synchronized kelondroRow.Entry get(byte[] key) { long handle = profile.startRead(); kelondroRow.Entry entry = null; - synchronized (buffer) { - entry = (kelondroRow.Entry) buffer.get(key); - if ((entry == null) && (useRowCollection)) entry = super.get(key); - } + entry = (kelondroRow.Entry) buffer.get(key); + if (entry == null) entry = store.get(key); profile.stopRead(handle); return entry; } - - public kelondroRow.Entry put(kelondroRow.Entry newentry) { + + public synchronized kelondroRow.Entry put(kelondroRow.Entry row, Date entryDate) { + return put(row); + } + + public synchronized kelondroRow.Entry put(kelondroRow.Entry newentry) { long handle = profile.startWrite(); - byte[] key = newentry.getColBytes(super.sortColumn); + byte[] key = newentry.getColBytes(store.sortColumn); kelondroRow.Entry oldentry = null; - synchronized (buffer) { - if (useRowCollection) { - oldentry = (kelondroRow.Entry) buffer.get(key); - if (oldentry == null) { - // try the collection - oldentry = super.get(key); - if (oldentry == null) { - // this was not anywhere - buffer.put(key, newentry); - if (((buffer.size() > bufferFlushMinimum) && (serverMemory.available() > memBlockLimit)) || - (buffer.size() > bufferFlushLimit)) flush(); - } else { - // replace old entry - super.put(newentry); - } - } else { - // the entry is already in buffer - // simply replace old entry - buffer.put(key, newentry); - } + oldentry = (kelondroRow.Entry) buffer.get(key); + if (oldentry == null) { + // try the collection + oldentry = store.get(key); + if (oldentry == null) { + // this was not anywhere + buffer.put(key, newentry); + if (((buffer.size() > bufferFlushMinimum) && (serverMemory.available() > memBlockLimit)) + || (buffer.size() > bufferFlushLimit)) + flush(); } else { - oldentry = (kelondroRow.Entry) buffer.put(key, newentry); + // replace old entry + store.put(newentry); } + } else { + // the entry is already in buffer + // simply replace old entry + buffer.put(key, newentry); } profile.stopWrite(handle); return oldentry; } - - public kelondroRow.Entry removeShift(byte[] key) { - long handle = profile.startDelete(); - kelondroRow.Entry oldentry = null; - synchronized (buffer) { - oldentry = (kelondroRow.Entry) buffer.remove(key); - if ((oldentry == null) && (useRowCollection)) { - // try the collection - oldentry = super.removeShift(key); - } - } - profile.stopDelete(handle); - return oldentry; - } - - public kelondroRow.Entry removeMarked(byte[] key) { + + public synchronized kelondroRow.Entry remove(byte[] key) { long handle = profile.startDelete(); kelondroRow.Entry oldentry = null; - synchronized (buffer) { - oldentry = (kelondroRow.Entry) buffer.remove(key); - if ((oldentry == null) && (useRowCollection)) { - // try the collection - return super.removeMarked(key); - } + oldentry = (kelondroRow.Entry) buffer.remove(key); + if (oldentry == null) { + // try the collection + return store.remove(key); } profile.stopDelete(handle); return oldentry; } - - public void removeMarkedAll(kelondroRowCollection c) { + + public synchronized void removeMarkedAll(kelondroRowCollection c) { long handle = profile.startDelete(); - synchronized (buffer) { - flush(); - super.removeMarkedAll(c); - } + flush(); + store.removeMarkedAll(c); profile.stopDelete(handle); } public kelondroProfile profile() { - return profile; + return store.profile(); + } + + public synchronized void close() { + flush(); + store.close(); + } + + public kelondroOrder order() { + return store.order(); + } + + public kelondroRow row() { + return store.row(); + } + + public Iterator rows(boolean up, boolean rotating, byte[] firstKey) { + return store.rows(up, rotating, firstKey); } - } diff --git a/source/de/anomic/kelondro/kelondroRowCollection.java b/source/de/anomic/kelondro/kelondroRowCollection.java index 6108f8e21..2ab15cfa3 100644 --- a/source/de/anomic/kelondro/kelondroRowCollection.java +++ b/source/de/anomic/kelondro/kelondroRowCollection.java @@ -44,10 +44,6 @@ public class kelondroRowCollection { private static final int exp_order_col = 4; private static final int exp_order_bound = 5; private static final int exp_collection = 6; - - public kelondroRowCollection(kelondroRow rowdef) { - this(rowdef, 0); - } public kelondroRowCollection(kelondroRowCollection rc) { this.rowdef = rc.rowdef; diff --git a/source/de/anomic/kelondro/kelondroRowSet.java b/source/de/anomic/kelondro/kelondroRowSet.java index 4eafe2efa..6d3892416 100644 --- a/source/de/anomic/kelondro/kelondroRowSet.java +++ b/source/de/anomic/kelondro/kelondroRowSet.java @@ -45,7 +45,7 @@ public class kelondroRowSet extends kelondroRowCollection implements kelondroInd } public kelondroRowSet(kelondroRow rowdef) { - super(rowdef); + super(rowdef, 0); this.removeMarker = new TreeSet(); this.profile = new kelondroProfile(); } @@ -106,13 +106,9 @@ public class kelondroRowSet extends kelondroRowCollection implements kelondroInd } public kelondroRow.Entry remove(byte[] a) { - return removeMarked(a); - } - - public kelondroRow.Entry removeMarked(byte[] a) { return removeMarked(a, 0, a.length); } - + private kelondroRow.Entry removeMarked(byte[] a, int astart, int alength) { if (chunkcount == 0) return null; long handle = profile.startDelete(); @@ -179,30 +175,6 @@ public class kelondroRowSet extends kelondroRowCollection implements kelondroInd chunkcount -= d; removeMarker.clear(); } - - - protected kelondroRow.Entry removeShift(byte[] a) { - return removeShift(a, 0, a.length); - } - - private kelondroRow.Entry removeShift(byte[] a, int astart, int alength) { - // the byte[] a may be shorter than the chunksize - if (chunkcount == 0) return null; - long handle = profile.startDelete(); - kelondroRow.Entry entry = null; - synchronized(chunkcache) { - int p = find(a, astart, alength); - if (p < 0) return null; - entry = get(p); - if (p < sortBound) { - removeShift(p); - } else { - super.swap(p, --chunkcount, 0); - } - } - profile.stopDelete(handle); - return entry; - } public void removeMarkedAll(kelondroRowCollection c) { long handle = profile.startDelete(); @@ -328,7 +300,7 @@ public class kelondroRowSet extends kelondroRowCollection implements kelondroInd return super.rows(); } - public Iterator rows(boolean up, boolean rotating, byte[] firstKey) throws IOException { + public Iterator rows(boolean up, boolean rotating, byte[] firstKey) { return new rowIterator(up, rotating, firstKey); }