Skip to content

Commit 5b07ee9

Browse files
the-sakthiApache9
authored andcommitted
HBASE-22086: Space Quota issue: Deleting snapshot doesn't update the usage of table
Signed-off-by: Duo Zhang <zhangduo@apache.org>
1 parent 4477dd5 commit 5b07ee9

4 files changed

Lines changed: 387 additions & 0 deletions

File tree

hbase-client/src/main/java/org/apache/hadoop/hbase/quotas/QuotaTableUtil.java

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,24 @@
2121
import java.io.ByteArrayInputStream;
2222
import java.io.ByteArrayOutputStream;
2323
import java.io.IOException;
24+
import java.util.ArrayList;
2425
import java.util.Collection;
2526
import java.util.HashMap;
27+
import java.util.HashSet;
2628
import java.util.List;
2729
import java.util.Map;
2830
import java.util.Objects;
31+
import java.util.Set;
2932
import java.util.regex.Pattern;
33+
3034
import org.apache.commons.lang3.StringUtils;
3135
import org.apache.hadoop.hbase.Cell;
3236
import org.apache.hadoop.hbase.CellScanner;
3337
import org.apache.hadoop.hbase.CompareOperator;
3438
import org.apache.hadoop.hbase.NamespaceDescriptor;
3539
import org.apache.hadoop.hbase.TableName;
3640
import org.apache.hadoop.hbase.client.Connection;
41+
import org.apache.hadoop.hbase.client.Delete;
3742
import org.apache.hadoop.hbase.client.Get;
3843
import org.apache.hadoop.hbase.client.Put;
3944
import org.apache.hadoop.hbase.client.Result;
@@ -53,6 +58,8 @@
5358
import org.slf4j.Logger;
5459
import org.slf4j.LoggerFactory;
5560

61+
import org.apache.hbase.thirdparty.com.google.common.collect.HashMultimap;
62+
import org.apache.hbase.thirdparty.com.google.common.collect.Multimap;
5663
import org.apache.hbase.thirdparty.com.google.protobuf.ByteString;
5764
import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException;
5865
import org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations;
@@ -540,6 +547,87 @@ static Put createPutForNamespaceSnapshotSize(String namespace, long size) {
540547
return p;
541548
}
542549

550+
/**
551+
* Returns a list of {@code Delete} to remove given table snapshot
552+
* entries to remove from quota table
553+
* @param snapshotEntriesToRemove the entries to remove
554+
*/
555+
static List<Delete> createDeletesForExistingTableSnapshotSizes(
556+
Multimap<TableName, String> snapshotEntriesToRemove) {
557+
List<Delete> deletes = new ArrayList<>();
558+
for (Map.Entry<TableName, Collection<String>> entry : snapshotEntriesToRemove.asMap()
559+
.entrySet()) {
560+
for (String snapshot : entry.getValue()) {
561+
Delete d = new Delete(getTableRowKey(entry.getKey()));
562+
d.addColumns(QUOTA_FAMILY_USAGE,
563+
Bytes.add(QUOTA_SNAPSHOT_SIZE_QUALIFIER, Bytes.toBytes(snapshot)));
564+
deletes.add(d);
565+
}
566+
}
567+
return deletes;
568+
}
569+
570+
/**
571+
* Returns a list of {@code Delete} to remove all table snapshot entries from quota table.
572+
* @param connection connection to re-use
573+
*/
574+
static List<Delete> createDeletesForExistingTableSnapshotSizes(Connection connection)
575+
throws IOException {
576+
return createDeletesForExistingSnapshotsFromScan(connection, createScanForSpaceSnapshotSizes());
577+
}
578+
579+
/**
580+
* Returns a list of {@code Delete} to remove given namespace snapshot
581+
* entries to removefrom quota table
582+
* @param snapshotEntriesToRemove the entries to remove
583+
*/
584+
static List<Delete> createDeletesForExistingNamespaceSnapshotSizes(
585+
Set<String> snapshotEntriesToRemove) {
586+
List<Delete> deletes = new ArrayList<>();
587+
for (String snapshot : snapshotEntriesToRemove) {
588+
Delete d = new Delete(getNamespaceRowKey(snapshot));
589+
d.addColumns(QUOTA_FAMILY_USAGE, QUOTA_SNAPSHOT_SIZE_QUALIFIER);
590+
deletes.add(d);
591+
}
592+
return deletes;
593+
}
594+
595+
/**
596+
* Returns a list of {@code Delete} to remove all namespace snapshot entries from quota table.
597+
* @param connection connection to re-use
598+
*/
599+
static List<Delete> createDeletesForExistingNamespaceSnapshotSizes(Connection connection)
600+
throws IOException {
601+
return createDeletesForExistingSnapshotsFromScan(connection,
602+
createScanForNamespaceSnapshotSizes());
603+
}
604+
605+
/**
606+
* Returns a list of {@code Delete} to remove all entries returned by the passed scanner.
607+
* @param connection connection to re-use
608+
* @param scan the scanner to use to generate the list of deletes
609+
*/
610+
static List<Delete> createDeletesForExistingSnapshotsFromScan(Connection connection, Scan scan)
611+
throws IOException {
612+
List<Delete> deletes = new ArrayList<>();
613+
try (Table quotaTable = connection.getTable(QUOTA_TABLE_NAME);
614+
ResultScanner rs = quotaTable.getScanner(scan)) {
615+
for (Result r : rs) {
616+
CellScanner cs = r.cellScanner();
617+
while (cs.advance()) {
618+
Cell c = cs.current();
619+
byte[] family = Bytes.copy(c.getFamilyArray(), c.getFamilyOffset(), c.getFamilyLength());
620+
byte[] qual =
621+
Bytes.copy(c.getQualifierArray(), c.getQualifierOffset(), c.getQualifierLength());
622+
Delete d = new Delete(r.getRow());
623+
d.addColumns(family, qual);
624+
deletes.add(d);
625+
}
626+
}
627+
return deletes;
628+
}
629+
}
630+
543631
/**
544632
* Fetches the computed size of all snapshots against tables in a namespace for space quotas.
545633
*/
@@ -575,6 +663,34 @@ static long parseSnapshotSize(Cell c) throws InvalidProtocolBufferException {
575663
return QuotaProtos.SpaceQuotaSnapshot.parseFrom(bs).getQuotaUsage();
576664
}
577665

666+
/**
667+
* Returns a scanner for all existing namespace snapshot entries.
668+
*/
669+
static Scan createScanForNamespaceSnapshotSizes() {
670+
return createScanForNamespaceSnapshotSizes(null);
671+
}
672+
673+
/**
674+
* Returns a scanner for all namespace snapshot entries of the given namespace
675+
* @param namespace name of the namespace whose snapshot entries are to be scanned
676+
*/
677+
static Scan createScanForNamespaceSnapshotSizes(String namespace) {
678+
Scan s = new Scan();
679+
if (namespace == null || namespace.isEmpty()) {
680+
// Read all namespaces, just look at the row prefix
681+
s.setRowPrefixFilter(QUOTA_NAMESPACE_ROW_KEY_PREFIX);
682+
} else {
683+
// Fetch the exact row for the table
684+
byte[] rowkey = getNamespaceRowKey(namespace);
685+
// Fetch just this one row
686+
s.withStartRow(rowkey).withStopRow(rowkey, true);
687+
}
688+
689+
// Just the usage family and only the snapshot size qualifiers
690+
return s.addFamily(QUOTA_FAMILY_USAGE)
691+
.setFilter(new ColumnPrefixFilter(QUOTA_SNAPSHOT_SIZE_QUALIFIER));
692+
}
693+
578694
static Scan createScanForSpaceSnapshotSizes() {
579695
return createScanForSpaceSnapshotSizes(null);
580696
}
@@ -621,6 +737,46 @@ public static Map<String,Long> getObservedSnapshotSizes(Connection conn) throws
621737
}
622738
}
623739

740+
/**
741+
* Returns a multimap for all existing table snapshot entries.
742+
* @param conn connection to re-use
743+
*/
744+
public static Multimap<TableName, String> getTableSnapshots(Connection conn) throws IOException {
745+
try (Table quotaTable = conn.getTable(QUOTA_TABLE_NAME);
746+
ResultScanner rs = quotaTable.getScanner(createScanForSpaceSnapshotSizes())) {
747+
Multimap<TableName, String> snapshots = HashMultimap.create();
748+
for (Result r : rs) {
749+
CellScanner cs = r.cellScanner();
750+
while (cs.advance()) {
751+
Cell c = cs.current();
752+
753+
final String snapshot = extractSnapshotNameFromSizeCell(c);
754+
snapshots.put(getTableFromRowKey(r.getRow()), snapshot);
755+
}
756+
}
757+
return snapshots;
758+
}
759+
}
760+
761+
/**
762+
* Returns a set of the names of all namespaces containing snapshot entries.
763+
* @param conn connection to re-use
764+
*/
765+
public static Set<String> getNamespaceSnapshots(Connection conn) throws IOException {
766+
try (Table quotaTable = conn.getTable(QUOTA_TABLE_NAME);
767+
ResultScanner rs = quotaTable.getScanner(createScanForNamespaceSnapshotSizes())) {
768+
Set<String> snapshots = new HashSet<>();
769+
for (Result r : rs) {
770+
CellScanner cs = r.cellScanner();
771+
while (cs.advance()) {
772+
cs.current();
773+
snapshots.add(getNamespaceFromRowKey(r.getRow()));
774+
}
775+
}
776+
return snapshots;
777+
}
778+
}
779+
624780
/**
625781
* Returns the current space quota snapshot of the given {@code tableName} from
626782
* {@code QuotaTableUtil.QUOTA_TABLE_NAME} or null if the no quota information is available for

hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/SnapshotQuotaObserverChore.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Collection;
2222
import java.util.HashMap;
2323
import java.util.HashSet;
24+
import java.util.List;
2425
import java.util.Map;
2526
import java.util.Map.Entry;
2627
import java.util.Set;
@@ -37,6 +38,7 @@
3738
import org.slf4j.LoggerFactory;
3839
import org.apache.hadoop.hbase.client.Admin;
3940
import org.apache.hadoop.hbase.client.Connection;
41+
import org.apache.hadoop.hbase.client.Delete;
4042
import org.apache.hadoop.hbase.client.Table;
4143
import org.apache.hadoop.hbase.master.HMaster;
4244
import org.apache.hadoop.hbase.master.MetricsMaster;
@@ -110,6 +112,12 @@ void _chore() throws IOException {
110112
metrics.incrementSnapshotFetchTime((System.nanoTime() - start) / 1_000_000);
111113
}
112114

115+
// Remove old table snapshots data
116+
pruneTableSnapshots(snapshotsToComputeSize);
117+
118+
// Remove old namespace snapshots data
119+
pruneNamespaceSnapshots(snapshotsToComputeSize);
120+
113121
// For each table, compute the size of each snapshot
114122
Map<String,Long> namespaceSnapshotSizes = computeSnapshotSizes(snapshotsToComputeSize);
115123

@@ -118,6 +126,43 @@ void _chore() throws IOException {
118126
persistSnapshotSizesForNamespaces(namespaceSnapshotSizes);
119127
}
120128

129+
/**
130+
* Removes the snapshot entries that are present in Quota table but not in snapshotsToComputeSize
131+
*
132+
* @param snapshotsToComputeSize list of snapshots to be persisted
133+
*/
134+
void pruneTableSnapshots(Multimap<TableName, String> snapshotsToComputeSize) throws IOException {
135+
Multimap<TableName, String> existingSnapshotEntries = QuotaTableUtil.getTableSnapshots(conn);
136+
Multimap<TableName, String> snapshotEntriesToRemove = HashMultimap.create();
137+
for (Entry<TableName, Collection<String>> entry : existingSnapshotEntries.asMap().entrySet()) {
138+
TableName tn = entry.getKey();
139+
Set<String> setOfSnapshots = new HashSet<>(entry.getValue());
140+
for (String snapshot : snapshotsToComputeSize.get(tn)) {
141+
setOfSnapshots.remove(snapshot);
142+
}
143+
144+
for (String snapshot : setOfSnapshots) {
145+
snapshotEntriesToRemove.put(tn, snapshot);
146+
}
147+
}
148+
removeExistingTableSnapshotSizes(snapshotEntriesToRemove);
149+
}
150+
151+
/**
152+
* Removes the snapshot entries that are present in Quota table but not in snapshotsToComputeSize
153+
*
154+
* @param snapshotsToComputeSize list of snapshots to be persisted
155+
*/
156+
void pruneNamespaceSnapshots(Multimap<TableName, String> snapshotsToComputeSize)
157+
throws IOException {
158+
Set<String> existingSnapshotEntries = QuotaTableUtil.getNamespaceSnapshots(conn);
159+
for (TableName tableName : snapshotsToComputeSize.keySet()) {
160+
existingSnapshotEntries.remove(tableName.getNamespaceAsString());
161+
}
162+
// here existingSnapshotEntries is left with the entries to be removed
163+
removeExistingNamespaceSnapshotSizes(existingSnapshotEntries);
164+
}
165+
121166
/**
122167
* Fetches each table with a quota (table or namespace quota), and then fetch the name of each
123168
* snapshot which was created from that table.
@@ -220,6 +265,24 @@ void persistSnapshotSizesForNamespaces(
220265
}
221266
}
222267

268+
void removeExistingTableSnapshotSizes(Multimap<TableName, String> snapshotEntriesToRemove)
269+
throws IOException {
270+
removeExistingSnapshotSizes(
271+
QuotaTableUtil.createDeletesForExistingTableSnapshotSizes(snapshotEntriesToRemove));
272+
}
273+
274+
void removeExistingNamespaceSnapshotSizes(Set<String> snapshotEntriesToRemove)
275+
throws IOException {
276+
removeExistingSnapshotSizes(
277+
QuotaTableUtil.createDeletesForExistingNamespaceSnapshotSizes(snapshotEntriesToRemove));
278+
}
279+
280+
void removeExistingSnapshotSizes(List<Delete> deletes) throws IOException {
281+
try (Table quotaTable = conn.getTable(QuotaUtil.QUOTA_TABLE_NAME)) {
282+
quotaTable.delete(deletes);
283+
}
284+
}
285+
223286
/**
224287
* Extracts the period for the chore from the configuration.
225288
*

0 commit comments

Comments
 (0)