001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.regionserver;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertNotNull;
022import static org.junit.Assert.assertNull;
023import static org.junit.Assert.assertTrue;
024
025import java.io.IOException;
026import java.util.ArrayList;
027import java.util.Arrays;
028import java.util.List;
029import java.util.NavigableMap;
030import java.util.Objects;
031import java.util.TreeMap;
032import java.util.concurrent.atomic.AtomicLong;
033import java.util.concurrent.atomic.AtomicReference;
034import org.apache.hadoop.conf.Configuration;
035import org.apache.hadoop.fs.Path;
036import org.apache.hadoop.hbase.Cell;
037import org.apache.hadoop.hbase.CellComparatorImpl;
038import org.apache.hadoop.hbase.CellUtil;
039import org.apache.hadoop.hbase.HBaseClassTestRule;
040import org.apache.hadoop.hbase.HBaseConfiguration;
041import org.apache.hadoop.hbase.HBaseTestingUtil;
042import org.apache.hadoop.hbase.HConstants;
043import org.apache.hadoop.hbase.KeepDeletedCells;
044import org.apache.hadoop.hbase.KeyValue;
045import org.apache.hadoop.hbase.KeyValueTestUtil;
046import org.apache.hadoop.hbase.KeyValueUtil;
047import org.apache.hadoop.hbase.TableDescriptors;
048import org.apache.hadoop.hbase.TableName;
049import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
050import org.apache.hadoop.hbase.client.Put;
051import org.apache.hadoop.hbase.client.RegionInfo;
052import org.apache.hadoop.hbase.client.RegionInfoBuilder;
053import org.apache.hadoop.hbase.client.Scan;
054import org.apache.hadoop.hbase.client.TableDescriptor;
055import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
056import org.apache.hadoop.hbase.exceptions.UnexpectedStateException;
057import org.apache.hadoop.hbase.testclassification.MediumTests;
058import org.apache.hadoop.hbase.testclassification.RegionServerTests;
059import org.apache.hadoop.hbase.util.Bytes;
060import org.apache.hadoop.hbase.util.EnvironmentEdge;
061import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
062import org.apache.hadoop.hbase.util.FSTableDescriptors;
063import org.apache.hadoop.hbase.wal.WALFactory;
064import org.junit.AfterClass;
065import org.junit.Before;
066import org.junit.ClassRule;
067import org.junit.Rule;
068import org.junit.Test;
069import org.junit.experimental.categories.Category;
070import org.junit.rules.TestName;
071import org.slf4j.Logger;
072import org.slf4j.LoggerFactory;
073
074import org.apache.hbase.thirdparty.com.google.common.base.Joiner;
075import org.apache.hbase.thirdparty.com.google.common.collect.Iterables;
076import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
077
078/** memstore test case */
079@Category({RegionServerTests.class, MediumTests.class})
080public class TestDefaultMemStore {
081
082  @ClassRule
083  public static final HBaseClassTestRule CLASS_RULE =
084      HBaseClassTestRule.forClass(TestDefaultMemStore.class);
085
086  private static final Logger LOG = LoggerFactory.getLogger(TestDefaultMemStore.class);
087  @Rule public TestName name = new TestName();
088  protected AbstractMemStore memstore;
089  protected static final int ROW_COUNT = 10;
090  protected static final int QUALIFIER_COUNT = ROW_COUNT;
091  protected static final byte[] FAMILY = Bytes.toBytes("column");
092  protected MultiVersionConcurrencyControl mvcc;
093  protected AtomicLong startSeqNum = new AtomicLong(0);
094  protected ChunkCreator chunkCreator;
095
096  private String getName() {
097    return this.name.getMethodName();
098  }
099
100  @Before
101  public void setUp() throws Exception {
102    internalSetUp();
103    // no pool
104    this.chunkCreator =
105      ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 0, 0,
106        0, null, MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT);
107    this.memstore = new DefaultMemStore();
108  }
109
110  @AfterClass
111  public static void tearDownClass() throws Exception {
112    ChunkCreator.getInstance().clearChunkIds();
113  }
114
115  protected void internalSetUp() throws Exception {
116    this.mvcc = new MultiVersionConcurrencyControl();
117  }
118
119  @Test
120  public void testPutSameKey() {
121    byte[] bytes = Bytes.toBytes(getName());
122    KeyValue kv = new KeyValue(bytes, bytes, bytes, bytes);
123    this.memstore.add(kv, null);
124    byte[] other = Bytes.toBytes("somethingelse");
125    KeyValue samekey = new KeyValue(bytes, bytes, bytes, other);
126    this.memstore.add(samekey, null);
127    Cell found = this.memstore.getActive().first();
128    assertEquals(1, this.memstore.getActive().getCellsCount());
129    assertTrue(Bytes.toString(found.getValueArray()), CellUtil.matchingValue(samekey, found));
130  }
131
132  @Test
133  public void testPutSameCell() {
134    byte[] bytes = Bytes.toBytes(getName());
135    KeyValue kv = new KeyValue(bytes, bytes, bytes, bytes);
136    MemStoreSizing sizeChangeForFirstCell = new NonThreadSafeMemStoreSizing();
137    this.memstore.add(kv, sizeChangeForFirstCell);
138    MemStoreSizing sizeChangeForSecondCell = new NonThreadSafeMemStoreSizing();
139    this.memstore.add(kv, sizeChangeForSecondCell);
140    // make sure memstore size increase won't double-count MSLAB chunk size
141    assertEquals(Segment.getCellLength(kv), sizeChangeForFirstCell.getMemStoreSize().getDataSize());
142    Segment segment = this.memstore.getActive();
143    MemStoreLAB msLab = segment.getMemStoreLAB();
144    if (msLab != null) {
145      // make sure memstore size increased even when writing the same cell, if using MSLAB
146      assertEquals(Segment.getCellLength(kv),
147          sizeChangeForSecondCell.getMemStoreSize().getDataSize());
148      // make sure chunk size increased even when writing the same cell, if using MSLAB
149      if (msLab instanceof MemStoreLABImpl) {
150        // since we add the chunkID at the 0th offset of the chunk and the
151        // chunkid is an int we need to account for those 4 bytes
152        assertEquals(2 * Segment.getCellLength(kv) + Bytes.SIZEOF_INT,
153          ((MemStoreLABImpl) msLab).getCurrentChunk().getNextFreeOffset());
154      }
155    } else {
156      // make sure no memstore size change w/o MSLAB
157      assertEquals(0, sizeChangeForSecondCell.getMemStoreSize().getDataSize());
158      assertEquals(0, sizeChangeForSecondCell.getMemStoreSize().getHeapSize());
159    }
160  }
161
162  /**
163   * Test memstore snapshot happening while scanning.
164   * @throws IOException
165   */
166  @Test
167  public void testScanAcrossSnapshot() throws IOException {
168    int rowCount = addRows(this.memstore);
169    List<KeyValueScanner> memstorescanners = this.memstore.getScanners(0);
170    Scan scan = new Scan();
171    List<Cell> result = new ArrayList<>();
172    Configuration conf = HBaseConfiguration.create();
173    ScanInfo scanInfo = new ScanInfo(conf, null, 0, 1, HConstants.LATEST_TIMESTAMP,
174        KeepDeletedCells.FALSE, HConstants.DEFAULT_BLOCKSIZE, 0, this.memstore.getComparator(), false);
175    int count = 0;
176    try (StoreScanner s = new StoreScanner(scan, scanInfo, null, memstorescanners)) {
177      while (s.next(result)) {
178        LOG.info(Objects.toString(result));
179        count++;
180        // Row count is same as column count.
181        assertEquals(rowCount, result.size());
182        result.clear();
183      }
184    }
185    assertEquals(rowCount, count);
186    for (KeyValueScanner scanner : memstorescanners) {
187      scanner.close();
188    }
189
190    memstorescanners = this.memstore.getScanners(mvcc.getReadPoint());
191    // Now assert can count same number even if a snapshot mid-scan.
192    count = 0;
193    try (StoreScanner s = new StoreScanner(scan, scanInfo, null, memstorescanners)) {
194      while (s.next(result)) {
195        LOG.info(Objects.toString(result));
196        // Assert the stuff is coming out in right order.
197        assertTrue(CellUtil.matchingRows(result.get(0), Bytes.toBytes(count)));
198        count++;
199        // Row count is same as column count.
200        assertEquals(rowCount, result.size());
201        if (count == 2) {
202          this.memstore.snapshot();
203          LOG.info("Snapshotted");
204        }
205        result.clear();
206      }
207    }
208    assertEquals(rowCount, count);
209    for (KeyValueScanner scanner : memstorescanners) {
210      scanner.close();
211    }
212    memstorescanners = this.memstore.getScanners(mvcc.getReadPoint());
213    // Assert that new values are seen in kvset as we scan.
214    long ts = EnvironmentEdgeManager.currentTime();
215    count = 0;
216    int snapshotIndex = 5;
217    try (StoreScanner s = new StoreScanner(scan, scanInfo, null, memstorescanners)) {
218      while (s.next(result)) {
219        LOG.info(Objects.toString(result));
220        // Assert the stuff is coming out in right order.
221        assertTrue(CellUtil.matchingRows(result.get(0), Bytes.toBytes(count)));
222        // Row count is same as column count.
223        assertEquals("count=" + count + ", result=" + result, rowCount, result.size());
224        count++;
225        if (count == snapshotIndex) {
226          MemStoreSnapshot snapshot = this.memstore.snapshot();
227          this.memstore.clearSnapshot(snapshot.getId());
228          // Added more rows into kvset. But the scanner wont see these rows.
229          addRows(this.memstore, ts);
230          LOG.info("Snapshotted, cleared it and then added values (which wont be seen)");
231        }
232        result.clear();
233      }
234    }
235    assertEquals(rowCount, count);
236  }
237
238  /**
239   * A simple test which verifies the 3 possible states when scanning across snapshot.
240   * @throws IOException
241   * @throws CloneNotSupportedException
242   */
243  @Test
244  public void testScanAcrossSnapshot2() throws IOException, CloneNotSupportedException {
245    // we are going to the scanning across snapshot with two kvs
246    // kv1 should always be returned before kv2
247    final byte[] one = Bytes.toBytes(1);
248    final byte[] two = Bytes.toBytes(2);
249    final byte[] f = Bytes.toBytes("f");
250    final byte[] q = Bytes.toBytes("q");
251    final byte[] v = Bytes.toBytes(3);
252
253    final KeyValue kv1 = new KeyValue(one, f, q, v);
254    final KeyValue kv2 = new KeyValue(two, f, q, v);
255
256    // use case 1: both kvs in kvset
257    this.memstore.add(kv1.clone(), null);
258    this.memstore.add(kv2.clone(), null);
259    // snapshot is empty,active segment is not empty,
260    // empty segment is skipped.
261    verifyOneScanAcrossSnapshot2(kv1, kv2);
262
263    // use case 2: both kvs in snapshot
264    // active segment is empty,snapshot is not empty,
265    // empty segment is skipped.
266    this.memstore.snapshot();
267    //
268    verifyOneScanAcrossSnapshot2(kv1, kv2);
269
270    // use case 3: first in snapshot second in kvset
271    this.memstore = new DefaultMemStore();
272    this.memstore.add(kv1.clone(), null);
273    this.memstore.snapshot();
274    this.memstore.add(kv2.clone(), null);
275    verifyScanAcrossSnapshot2(kv1, kv2);
276  }
277
278  protected void verifyScanAcrossSnapshot2(KeyValue kv1, KeyValue kv2)
279      throws IOException {
280    List<KeyValueScanner> memstorescanners = this.memstore.getScanners(mvcc.getReadPoint());
281    assertEquals(2, memstorescanners.size());
282    final KeyValueScanner scanner0 = memstorescanners.get(0);
283    final KeyValueScanner scanner1 = memstorescanners.get(1);
284    scanner0.seek(KeyValueUtil.createFirstOnRow(HConstants.EMPTY_START_ROW));
285    scanner1.seek(KeyValueUtil.createFirstOnRow(HConstants.EMPTY_START_ROW));
286    Cell n0 = scanner0.next();
287    Cell n1 = scanner1.next();
288    assertTrue(kv1.equals(n0) || kv1.equals(n1));
289    assertTrue(kv2.equals(n0)
290        || kv2.equals(n1)
291        || kv2.equals(scanner0.next())
292        || kv2.equals(scanner1.next()));
293    assertNull(scanner0.next());
294    assertNull(scanner1.next());
295  }
296
297  protected void verifyOneScanAcrossSnapshot2(KeyValue kv1, KeyValue kv2) throws IOException {
298    List<KeyValueScanner> memstorescanners = this.memstore.getScanners(mvcc.getReadPoint());
299    assertEquals(1, memstorescanners.size());
300    final KeyValueScanner scanner0 = memstorescanners.get(0);
301    scanner0.seek(KeyValueUtil.createFirstOnRow(HConstants.EMPTY_START_ROW));
302    Cell n0 = scanner0.next();
303    Cell n1 = scanner0.next();
304    assertTrue(kv1.equals(n0));
305    assertTrue(kv2.equals(n1));
306    assertNull(scanner0.next());
307  }
308
309  protected void assertScannerResults(KeyValueScanner scanner, KeyValue[] expected)
310      throws IOException {
311    scanner.seek(KeyValueUtil.createFirstOnRow(new byte[]{}));
312    List<Cell> returned = Lists.newArrayList();
313
314    while (true) {
315      Cell next = scanner.next();
316      if (next == null) break;
317      returned.add(next);
318    }
319
320    assertTrue(
321        "Got:\n" + Joiner.on("\n").join(returned) +
322        "\nExpected:\n" + Joiner.on("\n").join(expected),
323        Iterables.elementsEqual(Arrays.asList(expected), returned));
324    assertNull(scanner.peek());
325  }
326
327  @Test
328  public void testMemstoreConcurrentControl() throws IOException {
329    final byte[] row = Bytes.toBytes(1);
330    final byte[] f = Bytes.toBytes("family");
331    final byte[] q1 = Bytes.toBytes("q1");
332    final byte[] q2 = Bytes.toBytes("q2");
333    final byte[] v = Bytes.toBytes("value");
334
335    MultiVersionConcurrencyControl.WriteEntry w =
336        mvcc.begin();
337
338    KeyValue kv1 = new KeyValue(row, f, q1, v);
339    kv1.setSequenceId(w.getWriteNumber());
340    memstore.add(kv1, null);
341
342    KeyValueScanner s = this.memstore.getScanners(mvcc.getReadPoint()).get(0);
343    assertScannerResults(s, new KeyValue[]{});
344
345    mvcc.completeAndWait(w);
346
347    s = this.memstore.getScanners(mvcc.getReadPoint()).get(0);
348    assertScannerResults(s, new KeyValue[]{kv1});
349
350    w = mvcc.begin();
351    KeyValue kv2 = new KeyValue(row, f, q2, v);
352    kv2.setSequenceId(w.getWriteNumber());
353    memstore.add(kv2, null);
354
355    s = this.memstore.getScanners(mvcc.getReadPoint()).get(0);
356    assertScannerResults(s, new KeyValue[]{kv1});
357
358    mvcc.completeAndWait(w);
359
360    s = this.memstore.getScanners(mvcc.getReadPoint()).get(0);
361    assertScannerResults(s, new KeyValue[]{kv1, kv2});
362  }
363
364  /**
365   * Regression test for HBASE-2616, HBASE-2670.
366   * When we insert a higher-memstoreTS version of a cell but with
367   * the same timestamp, we still need to provide consistent reads
368   * for the same scanner.
369   */
370  @Test
371  public void testMemstoreEditsVisibilityWithSameKey() throws IOException {
372    final byte[] row = Bytes.toBytes(1);
373    final byte[] f = Bytes.toBytes("family");
374    final byte[] q1 = Bytes.toBytes("q1");
375    final byte[] q2 = Bytes.toBytes("q2");
376    final byte[] v1 = Bytes.toBytes("value1");
377    final byte[] v2 = Bytes.toBytes("value2");
378
379    // INSERT 1: Write both columns val1
380    MultiVersionConcurrencyControl.WriteEntry w =
381        mvcc.begin();
382
383    KeyValue kv11 = new KeyValue(row, f, q1, v1);
384    kv11.setSequenceId(w.getWriteNumber());
385    memstore.add(kv11, null);
386
387    KeyValue kv12 = new KeyValue(row, f, q2, v1);
388    kv12.setSequenceId(w.getWriteNumber());
389    memstore.add(kv12, null);
390    mvcc.completeAndWait(w);
391
392    // BEFORE STARTING INSERT 2, SEE FIRST KVS
393    KeyValueScanner s = this.memstore.getScanners(mvcc.getReadPoint()).get(0);
394    assertScannerResults(s, new KeyValue[]{kv11, kv12});
395
396    // START INSERT 2: Write both columns val2
397    w = mvcc.begin();
398    KeyValue kv21 = new KeyValue(row, f, q1, v2);
399    kv21.setSequenceId(w.getWriteNumber());
400    memstore.add(kv21, null);
401
402    KeyValue kv22 = new KeyValue(row, f, q2, v2);
403    kv22.setSequenceId(w.getWriteNumber());
404    memstore.add(kv22, null);
405
406    // BEFORE COMPLETING INSERT 2, SEE FIRST KVS
407    s = this.memstore.getScanners(mvcc.getReadPoint()).get(0);
408    assertScannerResults(s, new KeyValue[]{kv11, kv12});
409
410    // COMPLETE INSERT 2
411    mvcc.completeAndWait(w);
412
413    // NOW SHOULD SEE NEW KVS IN ADDITION TO OLD KVS.
414    // See HBASE-1485 for discussion about what we should do with
415    // the duplicate-TS inserts
416    s = this.memstore.getScanners(mvcc.getReadPoint()).get(0);
417    assertScannerResults(s, new KeyValue[]{kv21, kv11, kv22, kv12});
418  }
419
420  /**
421   * When we insert a higher-memstoreTS deletion of a cell but with
422   * the same timestamp, we still need to provide consistent reads
423   * for the same scanner.
424   */
425  @Test
426  public void testMemstoreDeletesVisibilityWithSameKey() throws IOException {
427    final byte[] row = Bytes.toBytes(1);
428    final byte[] f = Bytes.toBytes("family");
429    final byte[] q1 = Bytes.toBytes("q1");
430    final byte[] q2 = Bytes.toBytes("q2");
431    final byte[] v1 = Bytes.toBytes("value1");
432    // INSERT 1: Write both columns val1
433    MultiVersionConcurrencyControl.WriteEntry w =
434        mvcc.begin();
435
436    KeyValue kv11 = new KeyValue(row, f, q1, v1);
437    kv11.setSequenceId(w.getWriteNumber());
438    memstore.add(kv11, null);
439
440    KeyValue kv12 = new KeyValue(row, f, q2, v1);
441    kv12.setSequenceId(w.getWriteNumber());
442    memstore.add(kv12, null);
443    mvcc.completeAndWait(w);
444
445    // BEFORE STARTING INSERT 2, SEE FIRST KVS
446    KeyValueScanner s = this.memstore.getScanners(mvcc.getReadPoint()).get(0);
447    assertScannerResults(s, new KeyValue[]{kv11, kv12});
448
449    // START DELETE: Insert delete for one of the columns
450    w = mvcc.begin();
451    KeyValue kvDel = new KeyValue(row, f, q2, kv11.getTimestamp(),
452        KeyValue.Type.DeleteColumn);
453    kvDel.setSequenceId(w.getWriteNumber());
454    memstore.add(kvDel, null);
455
456    // BEFORE COMPLETING DELETE, SEE FIRST KVS
457    s = this.memstore.getScanners(mvcc.getReadPoint()).get(0);
458    assertScannerResults(s, new KeyValue[]{kv11, kv12});
459
460    // COMPLETE DELETE
461    mvcc.completeAndWait(w);
462
463    // NOW WE SHOULD SEE DELETE
464    s = this.memstore.getScanners(mvcc.getReadPoint()).get(0);
465    assertScannerResults(s, new KeyValue[]{kv11, kvDel, kv12});
466  }
467
468
469  private static class ReadOwnWritesTester extends Thread {
470    static final int NUM_TRIES = 1000;
471
472    final byte[] row;
473
474    final byte[] f = Bytes.toBytes("family");
475    final byte[] q1 = Bytes.toBytes("q1");
476
477    final MultiVersionConcurrencyControl mvcc;
478    final MemStore memstore;
479
480    AtomicReference<Throwable> caughtException;
481
482
483    public ReadOwnWritesTester(int id, MemStore memstore, MultiVersionConcurrencyControl mvcc,
484        AtomicReference<Throwable> caughtException) {
485      this.mvcc = mvcc;
486      this.memstore = memstore;
487      this.caughtException = caughtException;
488      row = Bytes.toBytes(id);
489    }
490
491    @Override
492    public void run() {
493      try {
494        internalRun();
495      } catch (Throwable t) {
496        caughtException.compareAndSet(null, t);
497      }
498    }
499
500    private void internalRun() throws IOException {
501      for (long i = 0; i < NUM_TRIES && caughtException.get() == null; i++) {
502        MultiVersionConcurrencyControl.WriteEntry w =
503            mvcc.begin();
504
505        // Insert the sequence value (i)
506        byte[] v = Bytes.toBytes(i);
507
508        KeyValue kv = new KeyValue(row, f, q1, i, v);
509        kv.setSequenceId(w.getWriteNumber());
510        memstore.add(kv, null);
511        mvcc.completeAndWait(w);
512
513        // Assert that we can read back
514        KeyValueScanner s = this.memstore.getScanners(mvcc.getReadPoint()).get(0);
515        s.seek(kv);
516
517        Cell ret = s.next();
518        assertNotNull("Didnt find own write at all", ret);
519        assertEquals("Didnt read own writes",
520                     kv.getTimestamp(), ret.getTimestamp());
521      }
522    }
523  }
524
525  @Test
526  public void testReadOwnWritesUnderConcurrency() throws Throwable {
527    int NUM_THREADS = 8;
528
529    ReadOwnWritesTester threads[] = new ReadOwnWritesTester[NUM_THREADS];
530    AtomicReference<Throwable> caught = new AtomicReference<>();
531
532    for (int i = 0; i < NUM_THREADS; i++) {
533      threads[i] = new ReadOwnWritesTester(i, memstore, mvcc, caught);
534      threads[i].start();
535    }
536
537    for (int i = 0; i < NUM_THREADS; i++) {
538      threads[i].join();
539    }
540
541    if (caught.get() != null) {
542      throw caught.get();
543    }
544  }
545
546  /**
547   * Test memstore snapshots
548   * @throws IOException
549   */
550  @Test
551  public void testSnapshotting() throws IOException {
552    final int snapshotCount = 5;
553    // Add some rows, run a snapshot. Do it a few times.
554    for (int i = 0; i < snapshotCount; i++) {
555      addRows(this.memstore);
556      runSnapshot(this.memstore);
557      assertEquals("History not being cleared", 0, this.memstore.getSnapshot().getCellsCount());
558    }
559  }
560
561  @Test
562  public void testMultipleVersionsSimple() throws Exception {
563    DefaultMemStore m = new DefaultMemStore(new Configuration(), CellComparatorImpl.COMPARATOR);
564    byte [] row = Bytes.toBytes("testRow");
565    byte [] family = Bytes.toBytes("testFamily");
566    byte [] qf = Bytes.toBytes("testQualifier");
567    long [] stamps = {1,2,3};
568    byte [][] values = {Bytes.toBytes("value0"), Bytes.toBytes("value1"),
569        Bytes.toBytes("value2")};
570    KeyValue key0 = new KeyValue(row, family, qf, stamps[0], values[0]);
571    KeyValue key1 = new KeyValue(row, family, qf, stamps[1], values[1]);
572    KeyValue key2 = new KeyValue(row, family, qf, stamps[2], values[2]);
573
574    m.add(key0, null);
575    m.add(key1, null);
576    m.add(key2, null);
577
578    assertTrue("Expected memstore to hold 3 values, actually has " +
579        m.getActive().getCellsCount(), m.getActive().getCellsCount() == 3);
580  }
581
582  //////////////////////////////////////////////////////////////////////////////
583  // Get tests
584  //////////////////////////////////////////////////////////////////////////////
585
586  /** Test getNextRow from memstore
587   * @throws InterruptedException
588   */
589  @Test
590  public void testGetNextRow() throws Exception {
591    addRows(this.memstore);
592    // Add more versions to make it a little more interesting.
593    Thread.sleep(1);
594    addRows(this.memstore);
595    Cell closestToEmpty = ((DefaultMemStore) this.memstore).getNextRow(KeyValue.LOWESTKEY);
596    assertTrue(CellComparatorImpl.COMPARATOR.compareRows(closestToEmpty,
597        new KeyValue(Bytes.toBytes(0), EnvironmentEdgeManager.currentTime())) == 0);
598    for (int i = 0; i < ROW_COUNT; i++) {
599      Cell nr = ((DefaultMemStore) this.memstore).getNextRow(new KeyValue(Bytes.toBytes(i),
600        EnvironmentEdgeManager.currentTime()));
601      if (i + 1 == ROW_COUNT) {
602        assertNull(nr);
603      } else {
604        assertTrue(CellComparatorImpl.COMPARATOR.compareRows(nr,
605            new KeyValue(Bytes.toBytes(i + 1), EnvironmentEdgeManager.currentTime())) == 0);
606      }
607    }
608    //starting from each row, validate results should contain the starting row
609    Configuration conf = HBaseConfiguration.create();
610    for (int startRowId = 0; startRowId < ROW_COUNT; startRowId++) {
611      ScanInfo scanInfo =
612          new ScanInfo(conf, FAMILY, 0, 1, Integer.MAX_VALUE, KeepDeletedCells.FALSE,
613              HConstants.DEFAULT_BLOCKSIZE, 0, this.memstore.getComparator(), false);
614      try (InternalScanner scanner =
615          new StoreScanner(new Scan().withStartRow(Bytes.toBytes(startRowId)), scanInfo, null,
616              memstore.getScanners(0))) {
617        List<Cell> results = new ArrayList<>();
618        for (int i = 0; scanner.next(results); i++) {
619          int rowId = startRowId + i;
620          Cell left = results.get(0);
621          byte[] row1 = Bytes.toBytes(rowId);
622          assertTrue("Row name",
623            CellComparatorImpl.COMPARATOR.compareRows(left, row1, 0, row1.length) == 0);
624          assertEquals("Count of columns", QUALIFIER_COUNT, results.size());
625          List<Cell> row = new ArrayList<>();
626          for (Cell kv : results) {
627            row.add(kv);
628          }
629          isExpectedRowWithoutTimestamps(rowId, row);
630          // Clear out set. Otherwise row results accumulate.
631          results.clear();
632        }
633      }
634    }
635  }
636
637  @Test
638  public void testGet_memstoreAndSnapShot() throws IOException {
639    byte [] row = Bytes.toBytes("testrow");
640    byte [] fam = Bytes.toBytes("testfamily");
641    byte [] qf1 = Bytes.toBytes("testqualifier1");
642    byte [] qf2 = Bytes.toBytes("testqualifier2");
643    byte [] qf3 = Bytes.toBytes("testqualifier3");
644    byte [] qf4 = Bytes.toBytes("testqualifier4");
645    byte [] qf5 = Bytes.toBytes("testqualifier5");
646    byte [] val = Bytes.toBytes("testval");
647
648    //Setting up memstore
649    memstore.add(new KeyValue(row, fam, qf1, val), null);
650    memstore.add(new KeyValue(row, fam, qf2, val), null);
651    memstore.add(new KeyValue(row, fam, qf3, val), null);
652    //Creating a snapshot
653    memstore.snapshot();
654    assertEquals(3, memstore.getSnapshot().getCellsCount());
655    //Adding value to "new" memstore
656    assertEquals(0, memstore.getActive().getCellsCount());
657    memstore.add(new KeyValue(row, fam ,qf4, val), null);
658    memstore.add(new KeyValue(row, fam ,qf5, val), null);
659    assertEquals(2, memstore.getActive().getCellsCount());
660  }
661
662  //////////////////////////////////////////////////////////////////////////////
663  // Delete tests
664  //////////////////////////////////////////////////////////////////////////////
665  @Test
666  public void testGetWithDelete() throws IOException {
667    byte [] row = Bytes.toBytes("testrow");
668    byte [] fam = Bytes.toBytes("testfamily");
669    byte [] qf1 = Bytes.toBytes("testqualifier");
670    byte [] val = Bytes.toBytes("testval");
671
672    long ts1 = System.nanoTime();
673    KeyValue put1 = new KeyValue(row, fam, qf1, ts1, val);
674    long ts2 = ts1 + 1;
675    KeyValue put2 = new KeyValue(row, fam, qf1, ts2, val);
676    long ts3 = ts2 + 1;
677    KeyValue put3 = new KeyValue(row, fam, qf1, ts3, val);
678    memstore.add(put1, null);
679    memstore.add(put2, null);
680    memstore.add(put3, null);
681
682    assertEquals(3, memstore.getActive().getCellsCount());
683
684    KeyValue del2 = new KeyValue(row, fam, qf1, ts2, KeyValue.Type.Delete, val);
685    memstore.add(del2, null);
686
687    List<Cell> expected = new ArrayList<>();
688    expected.add(put3);
689    expected.add(del2);
690    expected.add(put2);
691    expected.add(put1);
692
693    assertEquals(4, memstore.getActive().getCellsCount());
694    int i = 0;
695    for(Cell cell : memstore.getActive().getCellSet()) {
696      assertEquals(expected.get(i++), cell);
697    }
698  }
699
700  @Test
701  public void testGetWithDeleteColumn() throws IOException {
702    byte [] row = Bytes.toBytes("testrow");
703    byte [] fam = Bytes.toBytes("testfamily");
704    byte [] qf1 = Bytes.toBytes("testqualifier");
705    byte [] val = Bytes.toBytes("testval");
706
707    long ts1 = System.nanoTime();
708    KeyValue put1 = new KeyValue(row, fam, qf1, ts1, val);
709    long ts2 = ts1 + 1;
710    KeyValue put2 = new KeyValue(row, fam, qf1, ts2, val);
711    long ts3 = ts2 + 1;
712    KeyValue put3 = new KeyValue(row, fam, qf1, ts3, val);
713    memstore.add(put1, null);
714    memstore.add(put2, null);
715    memstore.add(put3, null);
716
717    assertEquals(3, memstore.getActive().getCellsCount());
718
719    KeyValue del2 =
720      new KeyValue(row, fam, qf1, ts2, KeyValue.Type.DeleteColumn, val);
721    memstore.add(del2, null);
722
723    List<Cell> expected = new ArrayList<>();
724    expected.add(put3);
725    expected.add(del2);
726    expected.add(put2);
727    expected.add(put1);
728
729    assertEquals(4, memstore.getActive().getCellsCount());
730    int i = 0;
731    for (Cell cell : memstore.getActive().getCellSet()) {
732      assertEquals(expected.get(i++), cell);
733    }
734  }
735
736  @Test
737  public void testGetWithDeleteFamily() throws IOException {
738    byte [] row = Bytes.toBytes("testrow");
739    byte [] fam = Bytes.toBytes("testfamily");
740    byte [] qf1 = Bytes.toBytes("testqualifier1");
741    byte [] qf2 = Bytes.toBytes("testqualifier2");
742    byte [] qf3 = Bytes.toBytes("testqualifier3");
743    byte [] val = Bytes.toBytes("testval");
744    long ts = System.nanoTime();
745
746    KeyValue put1 = new KeyValue(row, fam, qf1, ts, val);
747    KeyValue put2 = new KeyValue(row, fam, qf2, ts, val);
748    KeyValue put3 = new KeyValue(row, fam, qf3, ts, val);
749    KeyValue put4 = new KeyValue(row, fam, qf3, ts+1, val);
750
751    memstore.add(put1, null);
752    memstore.add(put2, null);
753    memstore.add(put3, null);
754    memstore.add(put4, null);
755
756    KeyValue del =
757      new KeyValue(row, fam, null, ts, KeyValue.Type.DeleteFamily, val);
758    memstore.add(del, null);
759
760    List<Cell> expected = new ArrayList<>();
761    expected.add(del);
762    expected.add(put1);
763    expected.add(put2);
764    expected.add(put4);
765    expected.add(put3);
766
767    assertEquals(5, memstore.getActive().getCellsCount());
768    int i = 0;
769    for (Cell cell : memstore.getActive().getCellSet()) {
770      assertEquals(expected.get(i++), cell);
771    }
772  }
773
774  @Test
775  public void testKeepDeleteInmemstore() {
776    byte [] row = Bytes.toBytes("testrow");
777    byte [] fam = Bytes.toBytes("testfamily");
778    byte [] qf = Bytes.toBytes("testqualifier");
779    byte [] val = Bytes.toBytes("testval");
780    long ts = System.nanoTime();
781    memstore.add(new KeyValue(row, fam, qf, ts, val), null);
782    KeyValue delete = new KeyValue(row, fam, qf, ts, KeyValue.Type.Delete, val);
783    memstore.add(delete, null);
784    assertEquals(2, memstore.getActive().getCellsCount());
785    assertEquals(delete, memstore.getActive().first());
786  }
787
788  @Test
789  public void testRetainsDeleteVersion() throws IOException {
790    // add a put to memstore
791    memstore.add(KeyValueTestUtil.create("row1", "fam", "a", 100, "dont-care"), null);
792
793    // now process a specific delete:
794    KeyValue delete = KeyValueTestUtil.create(
795        "row1", "fam", "a", 100, KeyValue.Type.Delete, "dont-care");
796    memstore.add(delete, null);
797
798    assertEquals(2, memstore.getActive().getCellsCount());
799    assertEquals(delete, memstore.getActive().first());
800  }
801
802  @Test
803  public void testRetainsDeleteColumn() throws IOException {
804    // add a put to memstore
805    memstore.add(KeyValueTestUtil.create("row1", "fam", "a", 100, "dont-care"), null);
806
807    // now process a specific delete:
808    KeyValue delete = KeyValueTestUtil.create("row1", "fam", "a", 100,
809        KeyValue.Type.DeleteColumn, "dont-care");
810    memstore.add(delete, null);
811
812    assertEquals(2, memstore.getActive().getCellsCount());
813    assertEquals(delete, memstore.getActive().first());
814  }
815
816  @Test
817  public void testRetainsDeleteFamily() throws IOException {
818    // add a put to memstore
819    memstore.add(KeyValueTestUtil.create("row1", "fam", "a", 100, "dont-care"), null);
820
821    // now process a specific delete:
822    KeyValue delete = KeyValueTestUtil.create("row1", "fam", "a", 100,
823        KeyValue.Type.DeleteFamily, "dont-care");
824    memstore.add(delete, null);
825
826    assertEquals(2, memstore.getActive().getCellsCount());
827    assertEquals(delete, memstore.getActive().first());
828  }
829
830  //////////////////////////////////////////////////////////////////////////////
831  // Helpers
832  //////////////////////////////////////////////////////////////////////////////
833  private static byte [] makeQualifier(final int i1, final int i2){
834    return Bytes.toBytes(Integer.toString(i1) + ";" +
835        Integer.toString(i2));
836  }
837
838  /**
839   * Add keyvalues with a fixed memstoreTs, and checks that memstore size is decreased
840   * as older keyvalues are deleted from the memstore.
841   * @throws Exception
842   */
843  @Test
844  public void testUpsertMemstoreSize() throws Exception {
845    Configuration conf = HBaseConfiguration.create();
846    memstore = new DefaultMemStore(conf, CellComparatorImpl.COMPARATOR);
847    MemStoreSize oldSize = memstore.size();
848
849    List<Cell> l = new ArrayList<>();
850    KeyValue kv1 = KeyValueTestUtil.create("r", "f", "q", 100, "v");
851    KeyValue kv2 = KeyValueTestUtil.create("r", "f", "q", 101, "v");
852    KeyValue kv3 = KeyValueTestUtil.create("r", "f", "q", 102, "v");
853
854    kv1.setSequenceId(1); kv2.setSequenceId(1);kv3.setSequenceId(1);
855    l.add(kv1); l.add(kv2); l.add(kv3);
856
857    this.memstore.upsert(l, 2, null);// readpoint is 2
858    MemStoreSize newSize = this.memstore.size();
859    assert (newSize.getDataSize() > oldSize.getDataSize());
860    //The kv1 should be removed.
861    assert(memstore.getActive().getCellsCount() == 2);
862
863    KeyValue kv4 = KeyValueTestUtil.create("r", "f", "q", 104, "v");
864    kv4.setSequenceId(1);
865    l.clear(); l.add(kv4);
866    this.memstore.upsert(l, 3, null);
867    assertEquals(newSize, this.memstore.size());
868    //The kv2 should be removed.
869    assert(memstore.getActive().getCellsCount() == 2);
870    //this.memstore = null;
871  }
872
873  ////////////////////////////////////
874  // Test for periodic memstore flushes
875  // based on time of oldest edit
876  ////////////////////////////////////
877
878  /**
879   * Tests that the timeOfOldestEdit is updated correctly for the
880   * various edit operations in memstore.
881   * @throws Exception
882   */
883  @Test
884  public void testUpdateToTimeOfOldestEdit() throws Exception {
885    try {
886      EnvironmentEdgeForMemstoreTest edge = new EnvironmentEdgeForMemstoreTest();
887      EnvironmentEdgeManager.injectEdge(edge);
888      DefaultMemStore memstore = new DefaultMemStore();
889      long t = memstore.timeOfOldestEdit();
890      assertEquals(Long.MAX_VALUE, t);
891
892      // test the case that the timeOfOldestEdit is updated after a KV add
893      memstore.add(KeyValueTestUtil.create("r", "f", "q", 100, "v"), null);
894      t = memstore.timeOfOldestEdit();
895      assertTrue(t == 1234);
896      // snapshot() will reset timeOfOldestEdit. The method will also assert the
897      // value is reset to Long.MAX_VALUE
898      t = runSnapshot(memstore);
899
900      // test the case that the timeOfOldestEdit is updated after a KV delete
901      memstore.add(KeyValueTestUtil.create("r", "f", "q", 100, KeyValue.Type.Delete, "v"), null);
902      t = memstore.timeOfOldestEdit();
903      assertTrue(t == 1234);
904      t = runSnapshot(memstore);
905
906      // test the case that the timeOfOldestEdit is updated after a KV upsert
907      List<Cell> l = new ArrayList<>();
908      KeyValue kv1 = KeyValueTestUtil.create("r", "f", "q", 100, "v");
909      kv1.setSequenceId(100);
910      l.add(kv1);
911      memstore.upsert(l, 1000, null);
912      t = memstore.timeOfOldestEdit();
913      assertTrue(t == 1234);
914    } finally {
915      EnvironmentEdgeManager.reset();
916    }
917  }
918
919  /**
920   * Tests the HRegion.shouldFlush method - adds an edit in the memstore
921   * and checks that shouldFlush returns true, and another where it disables
922   * the periodic flush functionality and tests whether shouldFlush returns
923   * false.
924   * @throws Exception
925   */
926  @Test
927  public void testShouldFlush() throws Exception {
928    Configuration conf = new Configuration();
929    conf.setInt(HRegion.MEMSTORE_PERIODIC_FLUSH_INTERVAL, 1000);
930    checkShouldFlush(conf, true);
931    // test disable flush
932    conf.setInt(HRegion.MEMSTORE_PERIODIC_FLUSH_INTERVAL, 0);
933    checkShouldFlush(conf, false);
934  }
935
936  protected void checkShouldFlush(Configuration conf, boolean expected) throws Exception {
937    try {
938      EnvironmentEdgeForMemstoreTest edge = new EnvironmentEdgeForMemstoreTest();
939      EnvironmentEdgeManager.injectEdge(edge);
940      HBaseTestingUtil hbaseUtility = new HBaseTestingUtil(conf);
941      String cf = "foo";
942      HRegion region =
943          hbaseUtility.createTestRegion("foobar", ColumnFamilyDescriptorBuilder.of(cf));
944
945      edge.setCurrentTimeMillis(1234);
946      Put p = new Put(Bytes.toBytes("r"));
947      p.add(KeyValueTestUtil.create("r", cf, "q", 100, "v"));
948      region.put(p);
949      edge.setCurrentTimeMillis(1234 + 100);
950      StringBuilder sb = new StringBuilder();
951      assertTrue(!region.shouldFlush(sb));
952      edge.setCurrentTimeMillis(1234 + 10000);
953      assertTrue(region.shouldFlush(sb) == expected);
954    } finally {
955      EnvironmentEdgeManager.reset();
956    }
957  }
958
959  @Test
960  public void testShouldFlushMeta() throws Exception {
961    // write an edit in the META and ensure the shouldFlush (that the periodic memstore
962    // flusher invokes) returns true after SYSTEM_CACHE_FLUSH_INTERVAL (even though
963    // the MEMSTORE_PERIODIC_FLUSH_INTERVAL is set to a higher value)
964    Configuration conf = new Configuration();
965    conf.setInt(HRegion.MEMSTORE_PERIODIC_FLUSH_INTERVAL, HRegion.SYSTEM_CACHE_FLUSH_INTERVAL * 10);
966    HBaseTestingUtil hbaseUtility = new HBaseTestingUtil(conf);
967    Path testDir = hbaseUtility.getDataTestDir();
968    EnvironmentEdgeForMemstoreTest edge = new EnvironmentEdgeForMemstoreTest();
969    EnvironmentEdgeManager.injectEdge(edge);
970    edge.setCurrentTimeMillis(1234);
971    WALFactory wFactory = new WALFactory(conf, "1234");
972    TableDescriptors tds = new FSTableDescriptors(conf);
973    FSTableDescriptors.tryUpdateMetaTableDescriptor(conf);
974    HRegion meta = HRegion.createHRegion(RegionInfoBuilder.FIRST_META_REGIONINFO, testDir,
975        conf, tds.get(TableName.META_TABLE_NAME),
976        wFactory.getWAL(RegionInfoBuilder.FIRST_META_REGIONINFO));
977    // parameterized tests add [#] suffix get rid of [ and ].
978    TableDescriptor desc = TableDescriptorBuilder
979        .newBuilder(TableName.valueOf(name.getMethodName().replaceAll("[\\[\\]]", "_")))
980        .setColumnFamily(ColumnFamilyDescriptorBuilder.of("foo")).build();
981    RegionInfo hri = RegionInfoBuilder.newBuilder(desc.getTableName())
982        .setStartKey(Bytes.toBytes("row_0200")).setEndKey(Bytes.toBytes("row_0300")).build();
983    HRegion r = HRegion.createHRegion(hri, testDir, conf, desc, wFactory.getWAL(hri));
984    addRegionToMETA(meta, r);
985    edge.setCurrentTimeMillis(1234 + 100);
986    StringBuilder sb = new StringBuilder();
987    assertTrue(meta.shouldFlush(sb) == false);
988    edge.setCurrentTimeMillis(edge.currentTime() + HRegion.SYSTEM_CACHE_FLUSH_INTERVAL + 1);
989    assertTrue(meta.shouldFlush(sb) == true);
990  }
991
992  /**
993   * Inserts a new region's meta information into the passed <code>meta</code> region.
994   * @param meta hbase:meta HRegion to be updated
995   * @param r HRegion to add to <code>meta</code>
996   */
997  private static void addRegionToMETA(final HRegion meta, final HRegion r) throws IOException {
998    // The row key is the region name
999    byte[] row = r.getRegionInfo().getRegionName();
1000    final long now = EnvironmentEdgeManager.currentTime();
1001    final List<Cell> cells = new ArrayList<>(2);
1002    cells.add(new KeyValue(row, HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER, now,
1003      RegionInfo.toByteArray(r.getRegionInfo())));
1004    // Set into the root table the version of the meta table.
1005    cells.add(new KeyValue(row, HConstants.CATALOG_FAMILY, HConstants.META_VERSION_QUALIFIER, now,
1006      Bytes.toBytes(HConstants.META_VERSION)));
1007    NavigableMap<byte[], List<Cell>> familyMap = new TreeMap<>(Bytes.BYTES_COMPARATOR);
1008    familyMap.put(HConstants.CATALOG_FAMILY, cells);
1009    meta.put(new Put(row, HConstants.LATEST_TIMESTAMP, familyMap));
1010  }
1011
1012  private class EnvironmentEdgeForMemstoreTest implements EnvironmentEdge {
1013    long t = 1234;
1014    @Override
1015    public long currentTime() {
1016      return t;
1017    }
1018    public void setCurrentTimeMillis(long t) {
1019      this.t = t;
1020    }
1021  }
1022
1023  /**
1024   * Adds {@link #ROW_COUNT} rows and {@link #QUALIFIER_COUNT}
1025   * @param hmc Instance to add rows to.
1026   * @return How many rows we added.
1027   * @throws IOException
1028   */
1029  protected int addRows(final AbstractMemStore hmc) {
1030    return addRows(hmc, HConstants.LATEST_TIMESTAMP);
1031  }
1032
1033  /**
1034   * Adds {@link #ROW_COUNT} rows and {@link #QUALIFIER_COUNT}
1035   * @param hmc Instance to add rows to.
1036   * @return How many rows we added.
1037   * @throws IOException
1038   */
1039  protected int addRows(final MemStore hmc, final long ts) {
1040    for (int i = 0; i < ROW_COUNT; i++) {
1041      long timestamp = ts == HConstants.LATEST_TIMESTAMP ?
1042        EnvironmentEdgeManager.currentTime() : ts;
1043      for (int ii = 0; ii < QUALIFIER_COUNT; ii++) {
1044        byte [] row = Bytes.toBytes(i);
1045        byte [] qf = makeQualifier(i, ii);
1046        hmc.add(new KeyValue(row, FAMILY, qf, timestamp, qf), null);
1047      }
1048    }
1049    return ROW_COUNT;
1050  }
1051
1052  private long runSnapshot(final AbstractMemStore hmc) throws UnexpectedStateException {
1053    // Save off old state.
1054    int oldHistorySize = hmc.getSnapshot().getCellsCount();
1055    MemStoreSnapshot snapshot = hmc.snapshot();
1056    // Make some assertions about what just happened.
1057    assertTrue("History size has not increased", oldHistorySize < hmc.getSnapshot().getCellsCount
1058        ());
1059    long t = memstore.timeOfOldestEdit();
1060    assertTrue("Time of oldest edit is not Long.MAX_VALUE", t == Long.MAX_VALUE);
1061    hmc.clearSnapshot(snapshot.getId());
1062    return t;
1063  }
1064
1065  private void isExpectedRowWithoutTimestamps(final int rowIndex,
1066      List<Cell> kvs) {
1067    int i = 0;
1068    for (Cell kv : kvs) {
1069      byte[] expectedColname = makeQualifier(rowIndex, i++);
1070      assertTrue("Column name", CellUtil.matchingQualifier(kv, expectedColname));
1071      // Value is column name as bytes.  Usually result is
1072      // 100 bytes in size at least. This is the default size
1073      // for BytesWriteable.  For comparison, convert bytes to
1074      // String and trim to remove trailing null bytes.
1075      assertTrue("Content", CellUtil.matchingValue(kv, expectedColname));
1076    }
1077  }
1078
1079  private static void addRows(int count, final MemStore mem) {
1080    long nanos = System.nanoTime();
1081
1082    for (int i = 0 ; i < count ; i++) {
1083      if (i % 1000 == 0) {
1084
1085        System.out.println(i + " Took for 1k usec: " + (System.nanoTime() - nanos)/1000);
1086        nanos = System.nanoTime();
1087      }
1088      long timestamp = System.currentTimeMillis();
1089
1090      for (int ii = 0; ii < QUALIFIER_COUNT ; ii++) {
1091        byte [] row = Bytes.toBytes(i);
1092        byte [] qf = makeQualifier(i, ii);
1093        mem.add(new KeyValue(row, FAMILY, qf, timestamp, qf), null);
1094      }
1095    }
1096  }
1097
1098  static void doScan(MemStore ms, int iteration) throws IOException {
1099    long nanos = System.nanoTime();
1100    KeyValueScanner s = ms.getScanners(0).get(0);
1101    s.seek(KeyValueUtil.createFirstOnRow(new byte[]{}));
1102
1103    System.out.println(iteration + " create/seek took: " + (System.nanoTime() - nanos)/1000);
1104    int cnt=0;
1105    while(s.next() != null) ++cnt;
1106
1107    System.out.println(iteration + " took usec: " + (System.nanoTime() - nanos) / 1000 + " for: "
1108        + cnt);
1109
1110  }
1111
1112  public static void main(String [] args) throws IOException {
1113    MemStore ms = new DefaultMemStore();
1114
1115    long n1 = System.nanoTime();
1116    addRows(25000, ms);
1117    System.out.println("Took for insert: " + (System.nanoTime()-n1)/1000);
1118
1119    System.out.println("foo");
1120
1121    for (int i = 0 ; i < 50 ; i++)
1122      doScan(ms, i);
1123  }
1124}
1125