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.wal;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertTrue;
023
024import java.util.HashMap;
025import java.util.HashSet;
026import java.util.Map;
027import java.util.Set;
028import org.apache.hadoop.hbase.HBaseClassTestRule;
029import org.apache.hadoop.hbase.HConstants;
030import org.apache.hadoop.hbase.testclassification.SmallTests;
031import org.apache.hadoop.hbase.util.Bytes;
032import org.junit.ClassRule;
033import org.junit.Test;
034import org.junit.experimental.categories.Category;
035
036@Category(SmallTests.class)
037public class TestSequenceIdAccounting {
038
039  @ClassRule
040  public static final HBaseClassTestRule CLASS_RULE =
041      HBaseClassTestRule.forClass(TestSequenceIdAccounting.class);
042
043  private static final byte [] ENCODED_REGION_NAME = Bytes.toBytes("r");
044  private static final byte [] FAMILY_NAME = Bytes.toBytes("cf");
045  private static final Set<byte[]> FAMILIES;
046  static {
047    FAMILIES = new HashSet<>();
048    FAMILIES.add(FAMILY_NAME);
049  }
050
051  @Test
052  public void testStartCacheFlush() {
053    SequenceIdAccounting sida = new SequenceIdAccounting();
054    sida.getOrCreateLowestSequenceIds(ENCODED_REGION_NAME);
055    Map<byte[], Long> m = new HashMap<>();
056    m.put(ENCODED_REGION_NAME, HConstants.NO_SEQNUM);
057    assertEquals(HConstants.NO_SEQNUM, (long)sida.startCacheFlush(ENCODED_REGION_NAME, FAMILIES));
058    sida.completeCacheFlush(ENCODED_REGION_NAME);
059    long sequenceid = 1;
060    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid, true);
061    // Only one family so should return NO_SEQNUM still.
062    assertEquals(HConstants.NO_SEQNUM, (long)sida.startCacheFlush(ENCODED_REGION_NAME, FAMILIES));
063    sida.completeCacheFlush(ENCODED_REGION_NAME);
064    long currentSequenceId = sequenceid;
065    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid, true);
066    final Set<byte[]> otherFamily = new HashSet<>(1);
067    otherFamily.add(Bytes.toBytes("otherCf"));
068    sida.update(ENCODED_REGION_NAME, FAMILIES, ++sequenceid, true);
069    // Should return oldest sequence id in the region.
070    assertEquals(currentSequenceId, (long)sida.startCacheFlush(ENCODED_REGION_NAME, otherFamily));
071    sida.completeCacheFlush(ENCODED_REGION_NAME);
072  }
073
074  @Test
075  public void testAreAllLower() {
076    SequenceIdAccounting sida = new SequenceIdAccounting();
077    sida.getOrCreateLowestSequenceIds(ENCODED_REGION_NAME);
078    Map<byte[], Long> m = new HashMap<>();
079    m.put(ENCODED_REGION_NAME, HConstants.NO_SEQNUM);
080    assertTrue(sida.areAllLower(m));
081    long sequenceid = 1;
082    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid, true);
083    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid++, true);
084    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid++, true);
085    assertTrue(sida.areAllLower(m));
086    m.put(ENCODED_REGION_NAME, sequenceid);
087    assertFalse(sida.areAllLower(m));
088    long lowest = sida.getLowestSequenceId(ENCODED_REGION_NAME);
089    assertEquals("Lowest should be first sequence id inserted", 1, lowest);
090    m.put(ENCODED_REGION_NAME, lowest);
091    assertFalse(sida.areAllLower(m));
092    // Now make sure above works when flushing.
093    sida.startCacheFlush(ENCODED_REGION_NAME, FAMILIES);
094    assertFalse(sida.areAllLower(m));
095    m.put(ENCODED_REGION_NAME, HConstants.NO_SEQNUM);
096    assertTrue(sida.areAllLower(m));
097    // Let the flush complete and if we ask if the sequenceid is lower, should be yes since no edits
098    sida.completeCacheFlush(ENCODED_REGION_NAME);
099    m.put(ENCODED_REGION_NAME, sequenceid);
100    assertTrue(sida.areAllLower(m));
101    // Flush again but add sequenceids while we are flushing.
102    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid++, true);
103    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid++, true);
104    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid++, true);
105    lowest = sida.getLowestSequenceId(ENCODED_REGION_NAME);
106    m.put(ENCODED_REGION_NAME, lowest);
107    assertFalse(sida.areAllLower(m));
108    sida.startCacheFlush(ENCODED_REGION_NAME, FAMILIES);
109    // The cache flush will clear out all sequenceid accounting by region.
110    assertEquals(HConstants.NO_SEQNUM, sida.getLowestSequenceId(ENCODED_REGION_NAME));
111    sida.completeCacheFlush(ENCODED_REGION_NAME);
112    // No new edits have gone in so no sequenceid to work with.
113    assertEquals(HConstants.NO_SEQNUM, sida.getLowestSequenceId(ENCODED_REGION_NAME));
114    // Make an edit behind all we'll put now into sida.
115    m.put(ENCODED_REGION_NAME, sequenceid);
116    sida.update(ENCODED_REGION_NAME, FAMILIES, ++sequenceid, true);
117    sida.update(ENCODED_REGION_NAME, FAMILIES, ++sequenceid, true);
118    sida.update(ENCODED_REGION_NAME, FAMILIES, ++sequenceid, true);
119    assertTrue(sida.areAllLower(m));
120  }
121
122  @Test
123  public void testFindLower() {
124    SequenceIdAccounting sida = new SequenceIdAccounting();
125    sida.getOrCreateLowestSequenceIds(ENCODED_REGION_NAME);
126    Map<byte[], Long> m = new HashMap<>();
127    m.put(ENCODED_REGION_NAME, HConstants.NO_SEQNUM);
128    long sequenceid = 1;
129    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid, true);
130    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid++, true);
131    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid++, true);
132    assertTrue(sida.findLower(m) == null);
133    m.put(ENCODED_REGION_NAME, sida.getLowestSequenceId(ENCODED_REGION_NAME));
134    assertTrue(sida.findLower(m).length == 1);
135    m.put(ENCODED_REGION_NAME, sida.getLowestSequenceId(ENCODED_REGION_NAME) - 1);
136    assertTrue(sida.findLower(m) == null);
137  }
138}