HBASE-13593 Quota support for namespace should take snapshot restore and clone into account (Ashish Singhi)
This commit is contained in:
parent
bbae9f3826
commit
c90da7a81c
|
@ -734,7 +734,16 @@ public class SnapshotManager extends MasterProcedureManager implements Stoppable
|
||||||
if (cpHost != null) {
|
if (cpHost != null) {
|
||||||
cpHost.preRestoreSnapshot(reqSnapshot, snapshotTableDesc);
|
cpHost.preRestoreSnapshot(reqSnapshot, snapshotTableDesc);
|
||||||
}
|
}
|
||||||
restoreSnapshot(snapshot, snapshotTableDesc);
|
try {
|
||||||
|
// Table already exist. Check and update the region quota for this table namespace
|
||||||
|
checkAndUpdateNamespaceRegionQuota(manifest, tableName);
|
||||||
|
restoreSnapshot(snapshot, snapshotTableDesc);
|
||||||
|
} catch (IOException e) {
|
||||||
|
this.master.getMasterQuotaManager().removeTableFromNamespaceQuota(tableName);
|
||||||
|
LOG.error("Exception occurred while restoring the snapshot " + snapshot.getName()
|
||||||
|
+ " as table " + tableName.getNameAsString(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
LOG.info("Restore snapshot=" + snapshot.getName() + " as table=" + tableName);
|
LOG.info("Restore snapshot=" + snapshot.getName() + " as table=" + tableName);
|
||||||
|
|
||||||
if (cpHost != null) {
|
if (cpHost != null) {
|
||||||
|
@ -745,7 +754,15 @@ public class SnapshotManager extends MasterProcedureManager implements Stoppable
|
||||||
if (cpHost != null) {
|
if (cpHost != null) {
|
||||||
cpHost.preCloneSnapshot(reqSnapshot, htd);
|
cpHost.preCloneSnapshot(reqSnapshot, htd);
|
||||||
}
|
}
|
||||||
cloneSnapshot(snapshot, htd);
|
try {
|
||||||
|
checkAndUpdateNamespaceQuota(manifest, tableName);
|
||||||
|
cloneSnapshot(snapshot, htd);
|
||||||
|
} catch (IOException e) {
|
||||||
|
this.master.getMasterQuotaManager().removeTableFromNamespaceQuota(tableName);
|
||||||
|
LOG.error("Exception occurred while cloning the snapshot " + snapshot.getName()
|
||||||
|
+ " as table " + tableName.getNameAsString(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
LOG.info("Clone snapshot=" + snapshot.getName() + " as table=" + tableName);
|
LOG.info("Clone snapshot=" + snapshot.getName() + " as table=" + tableName);
|
||||||
|
|
||||||
if (cpHost != null) {
|
if (cpHost != null) {
|
||||||
|
@ -754,6 +771,22 @@ public class SnapshotManager extends MasterProcedureManager implements Stoppable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkAndUpdateNamespaceQuota(SnapshotManifest manifest, TableName tableName)
|
||||||
|
throws IOException {
|
||||||
|
if (this.master.getMasterQuotaManager().isQuotaEnabled()) {
|
||||||
|
this.master.getMasterQuotaManager().checkNamespaceTableAndRegionQuota(tableName,
|
||||||
|
manifest.getRegionManifestsMap().size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAndUpdateNamespaceRegionQuota(SnapshotManifest manifest, TableName tableName)
|
||||||
|
throws IOException {
|
||||||
|
if (this.master.getMasterQuotaManager().isQuotaEnabled()) {
|
||||||
|
this.master.getMasterQuotaManager().checkAndUpdateNamespaceRegionQuota(tableName,
|
||||||
|
manifest.getRegionManifestsMap().size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restore the specified snapshot.
|
* Restore the specified snapshot.
|
||||||
* The restore will fail if the destination table has a snapshot or restore in progress.
|
* The restore will fail if the destination table has a snapshot or restore in progress.
|
||||||
|
|
|
@ -79,6 +79,20 @@ public class NamespaceAuditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check and update region count quota for an existing table.
|
||||||
|
* @param tName - table name for which region count to be updated.
|
||||||
|
* @param regions - Number of regions that will be added.
|
||||||
|
* @throws IOException Signals that an I/O exception has occurred.
|
||||||
|
*/
|
||||||
|
public void checkQuotaToUpdateRegion(TableName tName, int regions) throws IOException {
|
||||||
|
if (stateManager.isInitialized()) {
|
||||||
|
stateManager.checkAndUpdateNamespaceRegionCount(tName, regions);
|
||||||
|
} else {
|
||||||
|
checkTableTypeAndThrowException(tName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void checkTableTypeAndThrowException(TableName name) throws IOException {
|
private void checkTableTypeAndThrowException(TableName name) throws IOException {
|
||||||
if (name.isSystemTable()) {
|
if (name.isSystemTable()) {
|
||||||
LOG.debug("Namespace auditor checks not performed for table " + name.getNameAsString());
|
LOG.debug("Namespace auditor checks not performed for table " + name.getNameAsString());
|
||||||
|
|
|
@ -106,6 +106,33 @@ class NamespaceStateManager {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check and update region count for an existing table. To handle scenarios like restore snapshot
|
||||||
|
* @param TableName name of the table for region count needs to be checked and updated
|
||||||
|
* @param incr count of regions
|
||||||
|
* @throws QuotaExceededException if quota exceeds for the number of regions allowed in a
|
||||||
|
* namespace
|
||||||
|
* @throws IOException Signals that an I/O exception has occurred.
|
||||||
|
*/
|
||||||
|
synchronized void checkAndUpdateNamespaceRegionCount(TableName name, int incr)
|
||||||
|
throws IOException {
|
||||||
|
String namespace = name.getNamespaceAsString();
|
||||||
|
NamespaceDescriptor nspdesc = getNamespaceDescriptor(namespace);
|
||||||
|
if (nspdesc != null) {
|
||||||
|
NamespaceTableAndRegionInfo currentStatus = getState(namespace);
|
||||||
|
int regionCountOfTable = currentStatus.getRegionCountOfTable(name);
|
||||||
|
if ((currentStatus.getRegionCount() - regionCountOfTable + incr) > TableNamespaceManager
|
||||||
|
.getMaxRegions(nspdesc)) {
|
||||||
|
throw new QuotaExceededException("The table " + name.getNameAsString()
|
||||||
|
+ " region count cannot be updated as it would exceed maximum number "
|
||||||
|
+ "of regions allowed in the namespace. The total number of regions permitted is "
|
||||||
|
+ TableNamespaceManager.getMaxRegions(nspdesc));
|
||||||
|
}
|
||||||
|
currentStatus.removeTable(name);
|
||||||
|
currentStatus.addTable(name, incr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private NamespaceDescriptor getNamespaceDescriptor(String namespaceAsString) {
|
private NamespaceDescriptor getNamespaceDescriptor(String namespaceAsString) {
|
||||||
try {
|
try {
|
||||||
return this.master.getNamespaceDescriptor(namespaceAsString);
|
return this.master.getNamespaceDescriptor(namespaceAsString);
|
||||||
|
|
|
@ -313,6 +313,12 @@ public class MasterQuotaManager implements RegionStateListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void checkAndUpdateNamespaceRegionQuota(TableName tName, int regions) throws IOException {
|
||||||
|
if (enabled) {
|
||||||
|
namespaceQuotaManager.checkQuotaToUpdateRegion(tName, regions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void onRegionMerged(HRegionInfo hri) throws IOException {
|
public void onRegionMerged(HRegionInfo hri) throws IOException {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
namespaceQuotaManager.updateQuotaForRegionMerge(hri);
|
namespaceQuotaManager.updateQuotaForRegionMerge(hri);
|
||||||
|
|
|
@ -54,6 +54,7 @@ import org.apache.hadoop.hbase.client.ConnectionFactory;
|
||||||
import org.apache.hadoop.hbase.client.HBaseAdmin;
|
import org.apache.hadoop.hbase.client.HBaseAdmin;
|
||||||
import org.apache.hadoop.hbase.client.HTable;
|
import org.apache.hadoop.hbase.client.HTable;
|
||||||
import org.apache.hadoop.hbase.client.Mutation;
|
import org.apache.hadoop.hbase.client.Mutation;
|
||||||
|
import org.apache.hadoop.hbase.client.RegionLocator;
|
||||||
import org.apache.hadoop.hbase.client.Table;
|
import org.apache.hadoop.hbase.client.Table;
|
||||||
import org.apache.hadoop.hbase.coprocessor.BaseMasterObserver;
|
import org.apache.hadoop.hbase.coprocessor.BaseMasterObserver;
|
||||||
import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
|
import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
|
||||||
|
@ -76,6 +77,7 @@ import org.apache.hadoop.hbase.regionserver.HRegion;
|
||||||
import org.apache.hadoop.hbase.regionserver.HRegionServer;
|
import org.apache.hadoop.hbase.regionserver.HRegionServer;
|
||||||
import org.apache.hadoop.hbase.regionserver.Region;
|
import org.apache.hadoop.hbase.regionserver.Region;
|
||||||
import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost;
|
import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost;
|
||||||
|
import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
|
||||||
import org.apache.hadoop.hbase.testclassification.MediumTests;
|
import org.apache.hadoop.hbase.testclassification.MediumTests;
|
||||||
import org.apache.hadoop.hbase.util.Bytes;
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
import org.apache.hadoop.hbase.util.FSUtils;
|
import org.apache.hadoop.hbase.util.FSUtils;
|
||||||
|
@ -673,4 +675,131 @@ public class TestNamespaceAuditor {
|
||||||
ADMIN.createTable(tableDescOne);
|
ADMIN.createTable(tableDescOne);
|
||||||
ADMIN.createTable(tableDescTwo, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 4);
|
ADMIN.createTable(tableDescTwo, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = QuotaExceededException.class, timeout = 30000)
|
||||||
|
public void testCloneSnapshotQuotaExceed() throws Exception {
|
||||||
|
String nsp = prefix + "_testTableQuotaExceedWithCloneSnapshot";
|
||||||
|
NamespaceDescriptor nspDesc =
|
||||||
|
NamespaceDescriptor.create(nsp).addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "1")
|
||||||
|
.build();
|
||||||
|
ADMIN.createNamespace(nspDesc);
|
||||||
|
assertNotNull("Namespace descriptor found null.", ADMIN.getNamespaceDescriptor(nsp));
|
||||||
|
TableName tableName = TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1");
|
||||||
|
TableName cloneTableName = TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table2");
|
||||||
|
HTableDescriptor tableDescOne = new HTableDescriptor(tableName);
|
||||||
|
ADMIN.createTable(tableDescOne);
|
||||||
|
String snapshot = "snapshot_testTableQuotaExceedWithCloneSnapshot";
|
||||||
|
ADMIN.snapshot(snapshot, tableName);
|
||||||
|
ADMIN.cloneSnapshot(snapshot, cloneTableName);
|
||||||
|
ADMIN.deleteSnapshot(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 180000)
|
||||||
|
public void testCloneSnapshot() throws Exception {
|
||||||
|
String nsp = prefix + "_testCloneSnapshot";
|
||||||
|
NamespaceDescriptor nspDesc =
|
||||||
|
NamespaceDescriptor.create(nsp).addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "2")
|
||||||
|
.addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "20").build();
|
||||||
|
ADMIN.createNamespace(nspDesc);
|
||||||
|
assertNotNull("Namespace descriptor found null.", ADMIN.getNamespaceDescriptor(nsp));
|
||||||
|
TableName tableName = TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1");
|
||||||
|
TableName cloneTableName = TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table2");
|
||||||
|
HTableDescriptor tableDescOne = new HTableDescriptor(tableName);
|
||||||
|
|
||||||
|
ADMIN.createTable(tableDescOne, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 4);
|
||||||
|
String snapshot = "snapshot_testCloneSnapshot";
|
||||||
|
ADMIN.snapshot(snapshot, tableName);
|
||||||
|
ADMIN.cloneSnapshot(snapshot, cloneTableName);
|
||||||
|
|
||||||
|
int tableLength;
|
||||||
|
try (RegionLocator locator = ADMIN.getConnection().getRegionLocator(tableName)) {
|
||||||
|
tableLength = locator.getStartKeys().length;
|
||||||
|
}
|
||||||
|
assertEquals(tableName.getNameAsString() + " should have four regions.", 4, tableLength);
|
||||||
|
|
||||||
|
try (RegionLocator locator = ADMIN.getConnection().getRegionLocator(cloneTableName)) {
|
||||||
|
tableLength = locator.getStartKeys().length;
|
||||||
|
}
|
||||||
|
assertEquals(cloneTableName.getNameAsString() + " should have four regions.", 4, tableLength);
|
||||||
|
|
||||||
|
NamespaceTableAndRegionInfo nstate = getNamespaceState(nsp);
|
||||||
|
assertEquals("Total tables count should be 2.", 2, nstate.getTables().size());
|
||||||
|
assertEquals("Total regions count should be.", 8, nstate.getRegionCount());
|
||||||
|
|
||||||
|
ADMIN.deleteSnapshot(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 180000)
|
||||||
|
public void testRestoreSnapshot() throws Exception {
|
||||||
|
String nsp = prefix + "_testRestoreSnapshot";
|
||||||
|
NamespaceDescriptor nspDesc =
|
||||||
|
NamespaceDescriptor.create(nsp)
|
||||||
|
.addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "10").build();
|
||||||
|
ADMIN.createNamespace(nspDesc);
|
||||||
|
assertNotNull("Namespace descriptor found null.", ADMIN.getNamespaceDescriptor(nsp));
|
||||||
|
TableName tableName1 = TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1");
|
||||||
|
HTableDescriptor tableDescOne = new HTableDescriptor(tableName1);
|
||||||
|
ADMIN.createTable(tableDescOne, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 4);
|
||||||
|
|
||||||
|
NamespaceTableAndRegionInfo nstate = getNamespaceState(nsp);
|
||||||
|
assertEquals("Intial region count should be 4.", 4, nstate.getRegionCount());
|
||||||
|
|
||||||
|
String snapshot = "snapshot_testRestoreSnapshot";
|
||||||
|
ADMIN.snapshot(snapshot, tableName1);
|
||||||
|
|
||||||
|
List<HRegionInfo> regions = ADMIN.getTableRegions(tableName1);
|
||||||
|
Collections.sort(regions);
|
||||||
|
|
||||||
|
ADMIN.split(tableName1, Bytes.toBytes("JJJ"));
|
||||||
|
Thread.sleep(2000);
|
||||||
|
assertEquals("Total regions count should be 5.", 5, nstate.getRegionCount());
|
||||||
|
|
||||||
|
ADMIN.disableTable(tableName1);
|
||||||
|
ADMIN.restoreSnapshot(snapshot);
|
||||||
|
|
||||||
|
assertEquals("Total regions count should be 4 after restore.", 4, nstate.getRegionCount());
|
||||||
|
|
||||||
|
ADMIN.enableTable(tableName1);
|
||||||
|
ADMIN.deleteSnapshot(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 180000)
|
||||||
|
public void testRestoreSnapshotQuotaExceed() throws Exception {
|
||||||
|
String nsp = prefix + "_testRestoreSnapshotQuotaExceed";
|
||||||
|
NamespaceDescriptor nspDesc =
|
||||||
|
NamespaceDescriptor.create(nsp)
|
||||||
|
.addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "10").build();
|
||||||
|
ADMIN.createNamespace(nspDesc);
|
||||||
|
NamespaceDescriptor ndesc = ADMIN.getNamespaceDescriptor(nsp);
|
||||||
|
assertNotNull("Namespace descriptor found null.", ndesc);
|
||||||
|
TableName tableName1 = TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1");
|
||||||
|
HTableDescriptor tableDescOne = new HTableDescriptor(tableName1);
|
||||||
|
ADMIN.createTable(tableDescOne, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 4);
|
||||||
|
|
||||||
|
NamespaceTableAndRegionInfo nstate = getNamespaceState(nsp);
|
||||||
|
assertEquals("Intial region count should be 4.", 4, nstate.getRegionCount());
|
||||||
|
|
||||||
|
String snapshot = "snapshot_testRestoreSnapshotQuotaExceed";
|
||||||
|
ADMIN.snapshot(snapshot, tableName1);
|
||||||
|
|
||||||
|
List<HRegionInfo> regions = ADMIN.getTableRegions(tableName1);
|
||||||
|
Collections.sort(regions);
|
||||||
|
|
||||||
|
ADMIN.split(tableName1, Bytes.toBytes("JJJ"));
|
||||||
|
Thread.sleep(2000);
|
||||||
|
assertEquals("Total regions count should be 5.", 5, nstate.getRegionCount());
|
||||||
|
|
||||||
|
ndesc.setConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "2");
|
||||||
|
ADMIN.modifyNamespace(ndesc);
|
||||||
|
|
||||||
|
ADMIN.disableTable(tableName1);
|
||||||
|
try {
|
||||||
|
ADMIN.restoreSnapshot(snapshot);
|
||||||
|
fail("Region quota is exceeded so QuotaExceededException should be thrown but HBaseAdmin"
|
||||||
|
+ " wraps IOException into RestoreSnapshotException");
|
||||||
|
} catch (RestoreSnapshotException ignore) {
|
||||||
|
}
|
||||||
|
ADMIN.enableTable(tableName1);
|
||||||
|
ADMIN.deleteSnapshot(snapshot);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue