Skip to content

Commit 485e0d2

Browse files
authored
HBASE-24694 Support flush a single column family of table (#2179)
Signed-off-by: Wellington Chevreuil <wchevreuil@apache.org>
1 parent 9b49bd6 commit 485e0d2

14 files changed

Lines changed: 124 additions & 13 deletions

File tree

hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,16 @@ Future<Void> modifyColumnFamilyAsync(TableName tableName, ColumnFamilyDescriptor
513513
*/
514514
void flush(TableName tableName) throws IOException;
515515

516+
/**
517+
* Flush the specified column family stores on all regions of the passed table.
518+
* This runs as a synchronous operation.
519+
*
520+
* @param tableName table to flush
521+
* @param columnFamily column family within a table
522+
* @throws IOException if a remote or network exception occurs
523+
*/
524+
void flush(TableName tableName, byte[] columnFamily) throws IOException;
525+
516526
/**
517527
* Flush an individual region. Synchronous operation.
518528
*

hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminOverAsyncAdmin.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,11 @@ public void flush(TableName tableName) throws IOException {
244244
get(admin.flush(tableName));
245245
}
246246

247+
@Override
248+
public void flush(TableName tableName, byte[] columnFamily) throws IOException {
249+
get(admin.flush(tableName, columnFamily));
250+
}
251+
247252
@Override
248253
public void flushRegion(byte[] regionName) throws IOException {
249254
get(admin.flushRegion(regionName));

hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,14 @@ CompletableFuture<Void> modifyColumnFamily(TableName tableName,
302302
*/
303303
CompletableFuture<Void> flush(TableName tableName);
304304

305+
/**
306+
* Flush the specified column family stores on all regions of the passed table.
307+
* This runs as a synchronous operation.
308+
* @param tableName table to flush
309+
* @param columnFamily column family within a table
310+
*/
311+
CompletableFuture<Void> flush(TableName tableName, byte[] columnFamily);
312+
305313
/**
306314
* Flush an individual region.
307315
* @param regionName region to flush

hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,11 @@ public CompletableFuture<Void> flush(TableName tableName) {
243243
return wrap(rawAdmin.flush(tableName));
244244
}
245245

246+
@Override
247+
public CompletableFuture<Void> flush(TableName tableName, byte[] columnFamily) {
248+
return wrap(rawAdmin.flush(tableName, columnFamily));
249+
}
250+
246251
@Override
247252
public CompletableFuture<Void> flushRegion(byte[] regionName) {
248253
return wrap(rawAdmin.flushRegion(regionName));

hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -893,9 +893,13 @@ public CompletableFuture<List<RegionInfo>> getRegions(TableName tableName) {
893893
locs -> locs.stream().map(HRegionLocation::getRegion).collect(Collectors.toList()));
894894
}
895895
}
896-
897896
@Override
898897
public CompletableFuture<Void> flush(TableName tableName) {
898+
return flush(tableName, null);
899+
}
900+
901+
@Override
902+
public CompletableFuture<Void> flush(TableName tableName, byte[] columnFamily) {
899903
CompletableFuture<Void> future = new CompletableFuture<>();
900904
addListener(tableExists(tableName), (exists, err) -> {
901905
if (err != null) {
@@ -909,8 +913,12 @@ public CompletableFuture<Void> flush(TableName tableName) {
909913
} else if (!tableEnabled) {
910914
future.completeExceptionally(new TableNotEnabledException(tableName));
911915
} else {
916+
Map<String, String> props = new HashMap<>();
917+
if (columnFamily != null) {
918+
props.put(HConstants.FAMILY_KEY_STR, Bytes.toString(columnFamily));
919+
}
912920
addListener(execProcedure(FLUSH_TABLE_PROCEDURE_SIGNATURE, tableName.getNameAsString(),
913-
new HashMap<>()), (ret, err3) -> {
921+
props), (ret, err3) -> {
914922
if (err3 != null) {
915923
future.completeExceptionally(err3);
916924
} else {

hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,9 @@ public enum OperationStatusCode {
581581
*/
582582
public static final byte [] META_VERSION_QUALIFIER = Bytes.toBytes("v");
583583

584+
/** The family str as a key in map*/
585+
public static final String FAMILY_KEY_STR = "family";
586+
584587
/**
585588
* The current version of the meta table.
586589
* - pre-hbase 0.92. There is no META_VERSION column in the root table

hbase-server/src/main/java/org/apache/hadoop/hbase/procedure/flush/FlushTableSubprocedure.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*/
1818
package org.apache.hadoop.hbase.procedure.flush;
1919

20+
import java.util.Arrays;
2021
import java.util.List;
2122
import java.util.concurrent.Callable;
2223

@@ -28,7 +29,9 @@
2829
import org.apache.hadoop.hbase.procedure.ProcedureMember;
2930
import org.apache.hadoop.hbase.procedure.Subprocedure;
3031
import org.apache.hadoop.hbase.procedure.flush.RegionServerFlushTableProcedureManager.FlushTableSubprocedurePool;
32+
import org.apache.hadoop.hbase.regionserver.FlushLifeCycleTracker;
3133
import org.apache.hadoop.hbase.regionserver.HRegion;
34+
import org.apache.hadoop.hbase.util.Bytes;
3235

3336
/**
3437
* This flush region implementation uses the distributed procedure framework to flush
@@ -40,23 +43,27 @@ public class FlushTableSubprocedure extends Subprocedure {
4043
private static final Logger LOG = LoggerFactory.getLogger(FlushTableSubprocedure.class);
4144

4245
private final String table;
46+
private final String family;
4347
private final List<HRegion> regions;
4448
private final FlushTableSubprocedurePool taskManager;
4549

4650
public FlushTableSubprocedure(ProcedureMember member,
4751
ForeignExceptionDispatcher errorListener, long wakeFrequency, long timeout,
48-
List<HRegion> regions, String table,
52+
List<HRegion> regions, String table, String family,
4953
FlushTableSubprocedurePool taskManager) {
5054
super(member, table, errorListener, wakeFrequency, timeout);
5155
this.table = table;
56+
this.family = family;
5257
this.regions = regions;
5358
this.taskManager = taskManager;
5459
}
5560

5661
private static class RegionFlushTask implements Callable<Void> {
5762
HRegion region;
58-
RegionFlushTask(HRegion region) {
63+
List<byte[]> families;
64+
RegionFlushTask(HRegion region, List<byte[]> families) {
5965
this.region = region;
66+
this.families = families;
6067
}
6168

6269
@Override
@@ -65,7 +72,11 @@ public Void call() throws Exception {
6572
region.startRegionOperation();
6673
try {
6774
LOG.debug("Flush region " + region.toString() + " started...");
68-
region.flush(true);
75+
if (families == null) {
76+
region.flush(true);
77+
} else {
78+
region.flushcache(families, false, FlushLifeCycleTracker.DUMMY);
79+
}
6980
// TODO: flush result is not checked?
7081
} finally {
7182
LOG.debug("Closing region operation on " + region);
@@ -88,11 +99,15 @@ private void flushRegions() throws ForeignException {
8899
throw new IllegalStateException("Attempting to flush "
89100
+ table + " but we currently have outstanding tasks");
90101
}
91-
102+
List<byte[]> families = null;
103+
if (family != null) {
104+
LOG.debug("About to flush family {} on all regions for table {}", family, table);
105+
families = Arrays.asList(Bytes.toBytes(family));
106+
}
92107
// Add all hfiles already existing in region.
93108
for (HRegion region : regions) {
94109
// submit one task per region for parallelize by region.
95-
taskManager.submitTask(new RegionFlushTask(region));
110+
taskManager.submitTask(new RegionFlushTask(region, families));
96111
monitor.rethrowException();
97112
}
98113

hbase-server/src/main/java/org/apache/hadoop/hbase/procedure/flush/MasterFlushTableProcedureManager.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.concurrent.ThreadPoolExecutor;
2727
import org.apache.hadoop.conf.Configuration;
2828
import org.apache.hadoop.hbase.HBaseInterfaceAudience;
29+
import org.apache.hadoop.hbase.HConstants;
2930
import org.apache.hadoop.hbase.MetaTableAccessor;
3031
import org.apache.hadoop.hbase.ServerName;
3132
import org.apache.hadoop.hbase.TableName;
@@ -51,6 +52,7 @@
5152

5253
import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
5354

55+
import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
5456
import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.ProcedureDescription;
5557

5658
@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
@@ -149,11 +151,19 @@ public void execProcedure(ProcedureDescription desc) throws IOException {
149151

150152
ForeignExceptionDispatcher monitor = new ForeignExceptionDispatcher(desc.getInstance());
151153

154+
HBaseProtos.NameStringPair family = null;
155+
for (HBaseProtos.NameStringPair nsp : desc.getConfigurationList()) {
156+
if (HConstants.FAMILY_KEY_STR.equals(nsp.getName())) {
157+
family = nsp;
158+
}
159+
}
160+
byte[] procArgs = family != null ? family.toByteArray() : new byte[0];
161+
152162
// Kick of the global procedure from the master coordinator to the region servers.
153163
// We rely on the existing Distributed Procedure framework to prevent any concurrent
154164
// procedure with the same name.
155165
Procedure proc = coordinator.startProcedure(monitor, desc.getInstance(),
156-
new byte[0], Lists.newArrayList(regionServers));
166+
procArgs, Lists.newArrayList(regionServers));
157167
monitor.rethrowException();
158168
if (proc == null) {
159169
String msg = "Failed to submit distributed procedure " + desc.getSignature() + " for '"

hbase-server/src/main/java/org/apache/hadoop/hbase/procedure/flush/RegionServerFlushTableProcedureManager.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.apache.yetus.audience.InterfaceAudience;
5252
import org.slf4j.Logger;
5353
import org.slf4j.LoggerFactory;
54+
import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
5455

5556
/**
5657
* This manager class handles flushing of the regions for table on a {@link HRegionServer}.
@@ -129,9 +130,10 @@ public void stop(boolean force) throws IOException {
129130
* there is a possibility of a race where regions may be missed.
130131
*
131132
* @param table
133+
* @param family
132134
* @return Subprocedure to submit to the ProcedureMemeber.
133135
*/
134-
public Subprocedure buildSubprocedure(String table) {
136+
public Subprocedure buildSubprocedure(String table, String family) {
135137

136138
// don't run the subprocedure if the parent is stop(ping)
137139
if (rss.isStopping() || rss.isStopped()) {
@@ -162,7 +164,7 @@ public Subprocedure buildSubprocedure(String table) {
162164
FlushTableSubprocedurePool taskManager =
163165
new FlushTableSubprocedurePool(rss.getServerName().toString(), conf, rss);
164166
return new FlushTableSubprocedure(member, exnDispatcher, wakeMillis,
165-
timeoutMillis, involvedRegions, table, taskManager);
167+
timeoutMillis, involvedRegions, table, family, taskManager);
166168
}
167169

168170
/**
@@ -183,8 +185,19 @@ public class FlushTableSubprocedureBuilder implements SubprocedureFactory {
183185

184186
@Override
185187
public Subprocedure buildSubprocedure(String name, byte[] data) {
188+
String family = null;
189+
// Currently we do not put other data except family, so it is ok to
190+
// judge by length that if family was specified
191+
if (data.length > 0) {
192+
try {
193+
HBaseProtos.NameStringPair nsp = HBaseProtos.NameStringPair.parseFrom(data);
194+
family = nsp.getValue();
195+
} catch (Exception e) {
196+
LOG.error("fail to get family by parsing from data", e);
197+
}
198+
}
186199
// The name of the procedure instance from the master is the table name.
187-
return RegionServerFlushTableProcedureManager.this.buildSubprocedure(name);
200+
return RegionServerFlushTableProcedureManager.this.buildSubprocedure(name, family);
188201
}
189202

190203
}

hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFlushFromClient.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,32 @@ public void testFlushTable() throws Exception {
117117
}
118118
}
119119

120+
@Test
121+
public void testFlushTableFamily() throws Exception {
122+
try (Admin admin = TEST_UTIL.getAdmin()) {
123+
long sizeBeforeFlush = getRegionInfo().get(0).getMemStoreDataSize();
124+
admin.flush(tableName, FAMILY_1);
125+
assertFalse(getRegionInfo().stream().
126+
anyMatch(r -> r.getMemStoreDataSize() != sizeBeforeFlush / 2));
127+
}
128+
}
129+
120130
@Test
121131
public void testAsyncFlushTable() throws Exception {
122132
AsyncAdmin admin = asyncConn.getAdmin();
123133
admin.flush(tableName).get();
124134
assertFalse(getRegionInfo().stream().anyMatch(r -> r.getMemStoreDataSize() != 0));
125135
}
126136

137+
@Test
138+
public void testAsyncFlushTableFamily() throws Exception {
139+
AsyncAdmin admin = asyncConn.getAdmin();
140+
long sizeBeforeFlush = getRegionInfo().get(0).getMemStoreDataSize();
141+
admin.flush(tableName, FAMILY_1).get();
142+
assertFalse(getRegionInfo().stream().
143+
anyMatch(r -> r.getMemStoreDataSize() != sizeBeforeFlush / 2));
144+
}
145+
127146
@Test
128147
public void testFlushRegion() throws Exception {
129148
try (Admin admin = TEST_UTIL.getAdmin()) {

0 commit comments

Comments
 (0)