HBASE-26811 Secondary replica may be disabled for read forever (#4182)

This commit is contained in:
chenglei 2022-03-31 21:46:20 +08:00 committed by GitHub
parent 27111421d9
commit 805c346e54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 139 additions and 6 deletions

View File

@ -1335,11 +1335,7 @@ public class TableDescriptorBuilder {
* @return the modifyable TD * @return the modifyable TD
*/ */
public ModifyableTableDescriptor setRegionMemStoreReplication(boolean memstoreReplication) { public ModifyableTableDescriptor setRegionMemStoreReplication(boolean memstoreReplication) {
setValue(REGION_MEMSTORE_REPLICATION_KEY, Boolean.toString(memstoreReplication)); return setValue(REGION_MEMSTORE_REPLICATION_KEY, Boolean.toString(memstoreReplication));
// If the memstore replication is setup, we do not have to wait for observing a flush event
// from primary before starting to serve reads, because gaps from replication is not applicable
return setValue(REGION_REPLICA_WAIT_FOR_PRIMARY_FLUSH_CONF_KEY,
Boolean.toString(memstoreReplication));
} }
public ModifyableTableDescriptor setPriority(int priority) { public ModifyableTableDescriptor setPriority(int priority) {

View File

@ -23,6 +23,7 @@ import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.REGION_NAMES
import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.ROW_LOCK_READ_LOCK_KEY; import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.ROW_LOCK_READ_LOCK_KEY;
import static org.apache.hadoop.hbase.util.ConcurrentMapUtils.computeIfAbsent; import static org.apache.hadoop.hbase.util.ConcurrentMapUtils.computeIfAbsent;
import com.google.errorprone.annotations.RestrictedApi;
import edu.umd.cs.findbugs.annotations.Nullable; import edu.umd.cs.findbugs.annotations.Nullable;
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Span;
import java.io.EOFException; import java.io.EOFException;
@ -8750,4 +8751,10 @@ public class HRegion implements HeapSize, PropagatingConfigurationObserver, Regi
public void addWriteRequestsCount(long writeRequestsCount) { public void addWriteRequestsCount(long writeRequestsCount) {
this.writeRequestsCount.add(writeRequestsCount); this.writeRequestsCount.add(writeRequestsCount);
} }
@RestrictedApi(explanation = "Should only be called in tests", link = "",
allowedOnPath = ".*/src/test/.*")
boolean isReadsEnabled() {
return this.writestate.readsEnabled;
}
} }

View File

@ -2240,7 +2240,13 @@ public class HRegionServer extends HBaseServerBase<RSRpcServices>
} }
TableName tn = region.getTableDescriptor().getTableName(); TableName tn = region.getTableDescriptor().getTableName();
if (!ServerRegionReplicaUtil.isRegionReplicaReplicationEnabled(region.conf, tn) || if (!ServerRegionReplicaUtil.isRegionReplicaReplicationEnabled(region.conf, tn) ||
!ServerRegionReplicaUtil.isRegionReplicaWaitForPrimaryFlushEnabled(region.conf)) { !ServerRegionReplicaUtil.isRegionReplicaWaitForPrimaryFlushEnabled(region.conf) ||
// If the memstore replication not setup, we do not have to wait for observing a flush event
// from primary before starting to serve reads, because gaps from replication is not
// applicable,this logic is from
// TableDescriptorBuilder.ModifyableTableDescriptor.setRegionMemStoreReplication by
// HBASE-13063
!region.getTableDescriptor().hasRegionMemStoreReplication()) {
region.setReadsEnabled(true); region.setReadsEnabled(true);
return; return;
} }

View File

@ -0,0 +1,124 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.regionserver;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseClassTestRule;
import org.apache.hadoop.hbase.HBaseTestingUtil;
import org.apache.hadoop.hbase.StartTestingClusterOption;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.TableNameTestRule;
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
import org.apache.hadoop.hbase.client.TableDescriptor;
import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
import org.apache.hadoop.hbase.executor.ExecutorType;
import org.apache.hadoop.hbase.testclassification.MediumTests;
import org.apache.hadoop.hbase.testclassification.RegionServerTests;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Pair;
import org.apache.hadoop.hbase.util.ServerRegionReplicaUtil;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
@Category({ RegionServerTests.class, MediumTests.class })
public class TestRegionReplicaWaitForPrimaryFlushConf {
@ClassRule
public static final HBaseClassTestRule CLASS_RULE =
HBaseClassTestRule.forClass(TestRegionReplicaWaitForPrimaryFlushConf.class);
private static final byte[] FAMILY = Bytes.toBytes("family_test");
private TableName tableName;
@Rule
public final TableNameTestRule name = new TableNameTestRule();
private static final HBaseTestingUtil HTU = new HBaseTestingUtil();
@BeforeClass
public static void setUpBeforeClass() throws Exception {
Configuration conf = HTU.getConfiguration();
conf.setBoolean(ServerRegionReplicaUtil.REGION_REPLICA_REPLICATION_CONF_KEY, true);
conf.setBoolean(ServerRegionReplicaUtil.REGION_REPLICA_WAIT_FOR_PRIMARY_FLUSH_CONF_KEY, false);
HTU.startMiniCluster(StartTestingClusterOption.builder().numRegionServers(2).build());
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
HTU.shutdownMiniCluster();
}
/**
* This test is for HBASE-26811,when
* {@link ServerRegionReplicaUtil#REGION_REPLICA_WAIT_FOR_PRIMARY_FLUSH_CONF_KEY} is false and set
* {@link TableDescriptorBuilder#setRegionMemStoreReplication} to true explicitly,the secondary
* replica would be disabled for read after open.
*/
@Test
public void testSecondaryReplicaReadEnabled() throws Exception {
tableName = name.getTableName();
TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName)
.setRegionReplication(2).setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY))
.setRegionMemStoreReplication(true).build();
HTU.getAdmin().createTable(tableDescriptor);
final ArrayList<Pair<HRegion, HRegionServer>> regionAndRegionServers =
new ArrayList<Pair<HRegion, HRegionServer>>(Arrays.asList(null, null));
for (int i = 0; i < 2; i++) {
HRegionServer rs = HTU.getMiniHBaseCluster().getRegionServer(i);
List<HRegion> onlineRegions = rs.getRegions(tableName);
for (HRegion region : onlineRegions) {
int replicaId = region.getRegionInfo().getReplicaId();
assertNull(regionAndRegionServers.get(replicaId));
regionAndRegionServers.set(replicaId, new Pair<HRegion, HRegionServer>(region, rs));
}
}
for (Pair<HRegion, HRegionServer> pair : regionAndRegionServers) {
assertNotNull(pair);
}
HRegionServer secondaryRs = regionAndRegionServers.get(1).getSecond();
try {
secondaryRs.getExecutorService()
.getExecutorThreadPool(ExecutorType.RS_REGION_REPLICA_FLUSH_OPS);
fail();
} catch (NullPointerException e) {
assertTrue(e != null);
}
HRegion secondaryRegion = regionAndRegionServers.get(1).getFirst();
assertFalse(
ServerRegionReplicaUtil.isRegionReplicaWaitForPrimaryFlushEnabled(secondaryRegion.conf));
assertTrue(secondaryRegion.isReadsEnabled());
}
}