HBASE-26540 Implement a tool to verify the lag of region replication (#3950)
Signed-off-by: GeorryHuang <huangzhuoyue@apache.org>
This commit is contained in:
parent
5ac76c1c00
commit
9e469e4a2a
|
@ -0,0 +1,167 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import com.google.errorprone.annotations.RestrictedApi;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import org.apache.hadoop.conf.Configured;
|
||||||
|
import org.apache.hadoop.hbase.client.Admin;
|
||||||
|
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
|
||||||
|
import org.apache.hadoop.hbase.client.Connection;
|
||||||
|
import org.apache.hadoop.hbase.client.ConnectionFactory;
|
||||||
|
import org.apache.hadoop.hbase.client.ConnectionUtils;
|
||||||
|
import org.apache.hadoop.hbase.client.Consistency;
|
||||||
|
import org.apache.hadoop.hbase.client.Get;
|
||||||
|
import org.apache.hadoop.hbase.client.Put;
|
||||||
|
import org.apache.hadoop.hbase.client.Result;
|
||||||
|
import org.apache.hadoop.hbase.client.Table;
|
||||||
|
import org.apache.hadoop.hbase.client.TableDescriptor;
|
||||||
|
import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
|
||||||
|
import org.apache.hadoop.hbase.metrics.impl.FastLongHistogram;
|
||||||
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
|
import org.apache.hadoop.hbase.util.Threads;
|
||||||
|
import org.apache.hadoop.util.Tool;
|
||||||
|
import org.apache.hadoop.util.ToolRunner;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine;
|
||||||
|
import org.apache.hbase.thirdparty.org.apache.commons.cli.DefaultParser;
|
||||||
|
import org.apache.hbase.thirdparty.org.apache.commons.cli.HelpFormatter;
|
||||||
|
import org.apache.hbase.thirdparty.org.apache.commons.cli.Options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A tool to evaluating the lag between primary replica and secondary replica.
|
||||||
|
* <p/>
|
||||||
|
* It simply adds a row to the primary replica, and then check how long before we can read it from
|
||||||
|
* the secondary replica.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class RegionReplicationLagEvaluation extends Configured implements Tool {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RegionReplicationLagEvaluation.class);
|
||||||
|
|
||||||
|
public static final String TABLE_NAME = "TestLagTable";
|
||||||
|
|
||||||
|
public static final String FAMILY_NAME = "info";
|
||||||
|
|
||||||
|
public static final String QUALIFIER_NAME = "qual";
|
||||||
|
|
||||||
|
public static final int VALUE_LENGTH = 256;
|
||||||
|
|
||||||
|
public static final int ROW_LENGTH = 16;
|
||||||
|
|
||||||
|
private static final Options OPTIONS = new Options().addOption("t", "table", true, "Table name")
|
||||||
|
.addOption("rlen", "rlength", true, "The length of row key")
|
||||||
|
.addOption("vlen", "vlength", true, "The length of value")
|
||||||
|
.addRequiredOption("r", "rows", true, "Number of rows to test");
|
||||||
|
|
||||||
|
private FastLongHistogram histogram = new FastLongHistogram();
|
||||||
|
|
||||||
|
@RestrictedApi(explanation = "Should only be called in tests", link = "",
|
||||||
|
allowedOnPath = ".*/src/test/.*")
|
||||||
|
FastLongHistogram getHistogram() {
|
||||||
|
return histogram;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int run(String[] args) throws Exception {
|
||||||
|
TableName tableName;
|
||||||
|
int rlen;
|
||||||
|
int vlen;
|
||||||
|
int rows;
|
||||||
|
try {
|
||||||
|
CommandLine cli = new DefaultParser().parse(OPTIONS, args);
|
||||||
|
tableName = TableName.valueOf(cli.getOptionValue("t", TABLE_NAME));
|
||||||
|
rlen = Integer.parseInt(cli.getOptionValue("rlen", String.valueOf(ROW_LENGTH)));
|
||||||
|
vlen = Integer.parseInt(cli.getOptionValue("vlen", String.valueOf(VALUE_LENGTH)));
|
||||||
|
rows = Integer.parseInt(cli.getOptionValue("r"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.warn("Error parsing command line options", e);
|
||||||
|
HelpFormatter formatter = new HelpFormatter();
|
||||||
|
formatter.printHelp(getClass().getName(), OPTIONS);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
exec(tableName, rlen, vlen, rows);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTable(Admin admin, TableName tableName) throws IOException {
|
||||||
|
TableDescriptor td = TableDescriptorBuilder.newBuilder(tableName)
|
||||||
|
.setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY_NAME)).setRegionReplication(2)
|
||||||
|
.build();
|
||||||
|
admin.createTable(td);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkLag(Table table, int rlen, int vlen, int rows) throws IOException {
|
||||||
|
ThreadLocalRandom rand = ThreadLocalRandom.current();
|
||||||
|
byte[] family = Bytes.toBytes(FAMILY_NAME);
|
||||||
|
byte[] qualifier = Bytes.toBytes(QUALIFIER_NAME);
|
||||||
|
byte[] row = new byte[rlen];
|
||||||
|
byte[] value = new byte[vlen];
|
||||||
|
LOG.info("Test replication lag on table {} with {} rows", table.getName(), rows);
|
||||||
|
for (int i = 0; i < rows; i++) {
|
||||||
|
rand.nextBytes(row);
|
||||||
|
rand.nextBytes(value);
|
||||||
|
table.put(new Put(row).addColumn(family, qualifier, value));
|
||||||
|
// get from secondary replica
|
||||||
|
Get get = new Get(row).setConsistency(Consistency.TIMELINE).setReplicaId(1);
|
||||||
|
long startNs = System.nanoTime();
|
||||||
|
for (int retry = 0;; retry++) {
|
||||||
|
Result result = table.get(get);
|
||||||
|
byte[] gotValue = result.getValue(family, qualifier);
|
||||||
|
if (Arrays.equals(value, gotValue)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
long pauseTimeMs = Math.min(ConnectionUtils.getPauseTime(1, retry), 1000);
|
||||||
|
Threads.sleepWithoutInterrupt(pauseTimeMs);
|
||||||
|
}
|
||||||
|
long lagMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
|
||||||
|
histogram.add(lagMs, 1);
|
||||||
|
}
|
||||||
|
LOG.info("Test finished, min lag {} ms, max lag {} ms, mean lag {} ms", histogram.getMin(),
|
||||||
|
histogram.getMax(), histogram.getMean());
|
||||||
|
long[] q = histogram.getQuantiles(FastLongHistogram.DEFAULT_QUANTILES);
|
||||||
|
for (int i = 0; i < q.length; i++) {
|
||||||
|
LOG.info("{}% lag: {} ms", FastLongHistogram.DEFAULT_QUANTILES[i] * 100, q[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void exec(TableName tableName, int rlen, int vlen, int rows) throws IOException {
|
||||||
|
try (Connection conn = ConnectionFactory.createConnection(getConf())) {
|
||||||
|
try (Admin admin = conn.getAdmin()) {
|
||||||
|
if (!admin.tableExists(tableName)) {
|
||||||
|
createTable(admin, tableName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try (Table table = conn.getTable(tableName)) {
|
||||||
|
checkLag(table, rlen, vlen, rows);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
int res =
|
||||||
|
ToolRunner.run(HBaseConfiguration.create(), new RegionReplicationLagEvaluation(), args);
|
||||||
|
System.exit(res);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.metrics.impl.FastLongHistogram;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.MediumTests;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.MiscTests;
|
||||||
|
import org.apache.hadoop.hbase.util.ServerRegionReplicaUtil;
|
||||||
|
import org.apache.hadoop.util.ToolRunner;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
|
||||||
|
@Category({ MiscTests.class, MediumTests.class })
|
||||||
|
public class TestRegionReplicationLagEvaluation {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(TestRegionReplicationLagEvaluation.class);
|
||||||
|
|
||||||
|
private static final HBaseTestingUtil UTIL = new HBaseTestingUtil();
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setUp() throws Exception {
|
||||||
|
UTIL.getConfiguration().setBoolean(ServerRegionReplicaUtil.REGION_REPLICA_REPLICATION_CONF_KEY,
|
||||||
|
true);
|
||||||
|
UTIL.startMiniCluster(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void tearDown() throws Exception {
|
||||||
|
UTIL.shutdownMiniCluster();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() throws Exception {
|
||||||
|
RegionReplicationLagEvaluation tool = new RegionReplicationLagEvaluation();
|
||||||
|
assertEquals(0, ToolRunner.run(UTIL.getConfiguration(), tool, new String[] { "-r", "1000" }));
|
||||||
|
FastLongHistogram histogram = tool.getHistogram();
|
||||||
|
assertEquals(1000, histogram.getCount());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue