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}