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 byte[] META_FAMILY = Bytes.toBytes("METAFAMILY");
046  private static final Set<byte[]> FAMILIES;
047  private static final Set<byte[]> META_FAMILY_SET;
048  static {
049    FAMILIES = new HashSet<>();
050    FAMILIES.add(FAMILY_NAME);
051    META_FAMILY_SET = new HashSet<>();
052    META_FAMILY_SET.add(META_FAMILY);
053  }
054
055  @Test
056  public void testStartCacheFlush() {
057    SequenceIdAccounting sida = new SequenceIdAccounting();
058    sida.getOrCreateLowestSequenceIds(ENCODED_REGION_NAME);
059    Map<byte[], Long> m = new HashMap<>();
060    m.put(ENCODED_REGION_NAME, HConstants.NO_SEQNUM);
061    assertEquals(HConstants.NO_SEQNUM, (long) sida.startCacheFlush(ENCODED_REGION_NAME, FAMILIES));
062    sida.completeCacheFlush(ENCODED_REGION_NAME, HConstants.NO_SEQNUM);
063    long sequenceid = 1;
064    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid, true);
065    // Only one family so should return NO_SEQNUM still.
066    assertEquals(HConstants.NO_SEQNUM, (long) sida.startCacheFlush(ENCODED_REGION_NAME, FAMILIES));
067    sida.completeCacheFlush(ENCODED_REGION_NAME, HConstants.NO_SEQNUM);
068    long currentSequenceId = sequenceid;
069    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid, true);
070    final Set<byte[]> otherFamily = new HashSet<>(1);
071    otherFamily.add(Bytes.toBytes("otherCf"));
072    sida.update(ENCODED_REGION_NAME, FAMILIES, ++sequenceid, true);
073    // Should return oldest sequence id in the region.
074    assertEquals(currentSequenceId, (long) sida.startCacheFlush(ENCODED_REGION_NAME, otherFamily));
075    sida.completeCacheFlush(ENCODED_REGION_NAME, HConstants.NO_SEQNUM);
076  }
077
078  @Test
079  public void testAreAllLower() {
080    SequenceIdAccounting sida = new SequenceIdAccounting();
081    sida.getOrCreateLowestSequenceIds(ENCODED_REGION_NAME);
082    Map<byte[], Long> m = new HashMap<>();
083    m.put(ENCODED_REGION_NAME, HConstants.NO_SEQNUM);
084    assertTrue(sida.areAllLower(m));
085    long sequenceid = 1;
086    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid, true);
087    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid++, true);
088    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid++, true);
089    assertTrue(sida.areAllLower(m));
090    m.put(ENCODED_REGION_NAME, sequenceid);
091    assertFalse(sida.areAllLower(m));
092    long lowest = sida.getLowestSequenceId(ENCODED_REGION_NAME);
093    assertEquals("Lowest should be first sequence id inserted", 1, lowest);
094    m.put(ENCODED_REGION_NAME, lowest);
095    assertFalse(sida.areAllLower(m));
096    // Now make sure above works when flushing.
097    sida.startCacheFlush(ENCODED_REGION_NAME, FAMILIES);
098    assertFalse(sida.areAllLower(m));
099    m.put(ENCODED_REGION_NAME, HConstants.NO_SEQNUM);
100    assertTrue(sida.areAllLower(m));
101    // Let the flush complete and if we ask if the sequenceid is lower, should be yes since no edits
102    sida.completeCacheFlush(ENCODED_REGION_NAME, HConstants.NO_SEQNUM);
103    m.put(ENCODED_REGION_NAME, sequenceid);
104    assertTrue(sida.areAllLower(m));
105    // Flush again but add sequenceids while we are flushing.
106    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid++, true);
107    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid++, true);
108    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid++, true);
109    lowest = sida.getLowestSequenceId(ENCODED_REGION_NAME);
110    m.put(ENCODED_REGION_NAME, lowest);
111    assertFalse(sida.areAllLower(m));
112    sida.startCacheFlush(ENCODED_REGION_NAME, FAMILIES);
113    // The cache flush will clear out all sequenceid accounting by region.
114    assertEquals(HConstants.NO_SEQNUM, sida.getLowestSequenceId(ENCODED_REGION_NAME));
115    sida.completeCacheFlush(ENCODED_REGION_NAME, HConstants.NO_SEQNUM);
116    // No new edits have gone in so no sequenceid to work with.
117    assertEquals(HConstants.NO_SEQNUM, sida.getLowestSequenceId(ENCODED_REGION_NAME));
118    // Make an edit behind all we'll put now into sida.
119    m.put(ENCODED_REGION_NAME, sequenceid);
120    sida.update(ENCODED_REGION_NAME, FAMILIES, ++sequenceid, true);
121    sida.update(ENCODED_REGION_NAME, FAMILIES, ++sequenceid, true);
122    sida.update(ENCODED_REGION_NAME, FAMILIES, ++sequenceid, true);
123    assertTrue(sida.areAllLower(m));
124    m.put(ENCODED_REGION_NAME, sequenceid);
125    assertFalse(sida.areAllLower(m));
126
127    // Test the METAFAMILY is filtered in SequenceIdAccounting.lowestUnflushedSequenceIds
128    SequenceIdAccounting meta_sida = new SequenceIdAccounting();
129    Map<byte[], Long> meta_m = new HashMap<>();
130    meta_sida.getOrCreateLowestSequenceIds(ENCODED_REGION_NAME);
131    meta_m.put(ENCODED_REGION_NAME, sequenceid);
132    meta_sida.update(ENCODED_REGION_NAME, META_FAMILY_SET, ++sequenceid, true);
133    meta_sida.update(ENCODED_REGION_NAME, META_FAMILY_SET, ++sequenceid, true);
134    meta_sida.update(ENCODED_REGION_NAME, META_FAMILY_SET, ++sequenceid, true);
135    assertTrue(meta_sida.areAllLower(meta_m));
136    meta_m.put(ENCODED_REGION_NAME, sequenceid);
137    assertTrue(meta_sida.areAllLower(meta_m));
138  }
139
140  @Test
141  public void testFindLower() {
142    SequenceIdAccounting sida = new SequenceIdAccounting();
143    sida.getOrCreateLowestSequenceIds(ENCODED_REGION_NAME);
144    Map<byte[], Long> m = new HashMap<>();
145    m.put(ENCODED_REGION_NAME, HConstants.NO_SEQNUM);
146    long sequenceid = 1;
147    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid, true);
148    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid++, true);
149    sida.update(ENCODED_REGION_NAME, FAMILIES, sequenceid++, true);
150    assertTrue(sida.findLower(m) == null);
151    m.put(ENCODED_REGION_NAME, sida.getLowestSequenceId(ENCODED_REGION_NAME));
152    assertTrue(sida.findLower(m).size() == 1);
153    m.put(ENCODED_REGION_NAME, sida.getLowestSequenceId(ENCODED_REGION_NAME) - 1);
154    assertTrue(sida.findLower(m) == null);
155  }
156}