Skip to content

Commit 4678de1

Browse files
terrytluguluo2016
andcommitted
HBASE-29272 Fix TableSnapshotInputFormatImpl.InputSplit.getLength() returning 0 on branch-2.6
When Spark reads a snapshot, InputSplit.getLength() always returned 0, so the distributed execution framework skipped the split and the job produced empty/incomplete data. Compute the actual region size via SnapshotRegionSizeCalculator (which returns a new RegionSizes holder) and use it as the split length. Per the HBase API stability policy, the public 5-arg TableSnapshotRegionSplit constructor is retained but marked @deprecated (since 2.6.7, to be removed in 4.0.0), matching the removal already done on master. Co-authored-by: Peng Lu <lupeng@apache.org> Signed-off-by: terrytlu <terrytlu@tencent.com> Signed-off-by: Peng Lu <lupeng@apache.org>
1 parent 35dd29b commit 4678de1

7 files changed

Lines changed: 298 additions & 7 deletions

File tree

hbase-mapreduce/src/main/java/org/apache/hadoop/hbase/mapred/TableSnapshotInputFormat.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,33 @@ public TableSnapshotRegionSplit(TableSnapshotInputFormatImpl.InputSplit delegate
5656
this.delegate = delegate;
5757
}
5858

59+
/**
60+
* @deprecated since 2.6.7 and will be removed in 4.0.0.
61+
* Use {@link #TableSnapshotRegionSplit(TableSnapshotInputFormatImpl.InputSplit)} instead.
62+
* @see <a href="https://issues.apache.org/jira/browse/HBASE-29272">HBASE-29272</a>
63+
*/
64+
@Deprecated
5965
public TableSnapshotRegionSplit(HTableDescriptor htd, HRegionInfo regionInfo,
6066
List<String> locations, Scan scan, Path restoreDir) {
67+
this(htd, regionInfo, locations, scan, restoreDir, 1);
68+
}
69+
70+
/**
71+
* Creates a TableSnapshotRegionSplit with the given region information and the estimated length
72+
* of the region in bytes. The length is used by the MapReduce framework to balance the
73+
* distribution of splits; a value of 0 may cause the framework to skip the split.
74+
* @param htd the table descriptor
75+
* @param regionInfo the region info
76+
* @param locations the locations of the region
77+
* @param scan the scan to use
78+
* @param restoreDir the directory to restore the snapshot to
79+
* @param length the estimated size of the region in bytes
80+
* @see <a href="https://issues.apache.org/jira/browse/HBASE-29272">HBASE-29272</a>
81+
*/
82+
public TableSnapshotRegionSplit(HTableDescriptor htd, HRegionInfo regionInfo,
83+
List<String> locations, Scan scan, Path restoreDir, long length) {
6184
this.delegate =
62-
new TableSnapshotInputFormatImpl.InputSplit(htd, regionInfo, locations, scan, restoreDir);
85+
new TableSnapshotInputFormatImpl.InputSplit(htd, regionInfo, locations, scan, restoreDir, length);
6386
}
6487

6588
@Override

hbase-mapreduce/src/main/java/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormat.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,33 @@ public TableSnapshotRegionSplit(TableSnapshotInputFormatImpl.InputSplit delegate
9595
this.delegate = delegate;
9696
}
9797

98+
/**
99+
* @deprecated since 2.6.7 and will be removed in 4.0.0.
100+
* Use {@link #TableSnapshotRegionSplit(TableSnapshotInputFormatImpl.InputSplit)} instead.
101+
* @see <a href="https://issues.apache.org/jira/browse/HBASE-29272">HBASE-29272</a>
102+
*/
103+
@Deprecated
98104
public TableSnapshotRegionSplit(HTableDescriptor htd, HRegionInfo regionInfo,
99105
List<String> locations, Scan scan, Path restoreDir) {
106+
this(htd, regionInfo, locations, scan, restoreDir, 1);
107+
}
108+
109+
/**
110+
* Creates a TableSnapshotRegionSplit with the given region information and the estimated length
111+
* of the region in bytes. The length is used by the MapReduce framework to balance the
112+
* distribution of splits; a value of 0 may cause the framework to skip the split.
113+
* @param htd the table descriptor
114+
* @param regionInfo the region info
115+
* @param locations the locations of the region
116+
* @param scan the scan to use
117+
* @param restoreDir the directory to restore the snapshot to
118+
* @param length the estimated size of the region in bytes
119+
* @see <a href="https://issues.apache.org/jira/browse/HBASE-29272">HBASE-29272</a>
120+
*/
121+
public TableSnapshotRegionSplit(HTableDescriptor htd, HRegionInfo regionInfo,
122+
List<String> locations, Scan scan, Path restoreDir, long length) {
100123
this.delegate =
101-
new TableSnapshotInputFormatImpl.InputSplit(htd, regionInfo, locations, scan, restoreDir);
124+
new TableSnapshotInputFormatImpl.InputSplit(htd, regionInfo, locations, scan, restoreDir, length);
102125
}
103126

104127
@Override

hbase-mapreduce/src/main/java/org/apache/hadoop/hbase/mapreduce/TableSnapshotInputFormatImpl.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.io.ByteArrayOutputStream;
2121
import java.io.DataInput;
2222
import java.io.DataOutput;
23+
import java.io.EOFException;
2324
import java.io.IOException;
2425
import java.lang.reflect.InvocationTargetException;
2526
import java.util.ArrayList;
@@ -45,8 +46,10 @@
4546
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
4647
import org.apache.hadoop.hbase.regionserver.HRegion;
4748
import org.apache.hadoop.hbase.snapshot.RestoreSnapshotHelper;
49+
import org.apache.hadoop.hbase.snapshot.RegionSizes;
4850
import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
4951
import org.apache.hadoop.hbase.snapshot.SnapshotManifest;
52+
import org.apache.hadoop.hbase.snapshot.SnapshotRegionSizeCalculator;
5053
import org.apache.hadoop.hbase.util.Bytes;
5154
import org.apache.hadoop.hbase.util.CommonFSUtils;
5255
import org.apache.hadoop.hbase.util.RegionSplitter;
@@ -145,13 +148,14 @@ public static class InputSplit implements Writable {
145148
private String[] locations;
146149
private String scan;
147150
private String restoreDir;
151+
private long length;
148152

149153
// constructor for mapreduce framework / Writable
150154
public InputSplit() {
151155
}
152156

153157
public InputSplit(TableDescriptor htd, HRegionInfo regionInfo, List<String> locations,
154-
Scan scan, Path restoreDir) {
158+
Scan scan, Path restoreDir, long length) {
155159
this.htd = htd;
156160
this.regionInfo = regionInfo;
157161
if (locations == null || locations.isEmpty()) {
@@ -166,6 +170,7 @@ public InputSplit(TableDescriptor htd, HRegionInfo regionInfo, List<String> loca
166170
}
167171

168172
this.restoreDir = restoreDir.toString();
173+
this.length = length;
169174
}
170175

171176
public TableDescriptor getHtd() {
@@ -181,8 +186,7 @@ public String getRestoreDir() {
181186
}
182187

183188
public long getLength() {
184-
// TODO: We can obtain the file sizes of the snapshot here.
185-
return 0;
189+
return length;
186190
}
187191

188192
public String[] getLocations() {
@@ -219,6 +223,7 @@ public void write(DataOutput out) throws IOException {
219223

220224
Bytes.writeByteArray(out, Bytes.toBytes(scan));
221225
Bytes.writeByteArray(out, Bytes.toBytes(restoreDir));
226+
out.writeLong(length);
222227

223228
}
224229

@@ -235,6 +240,12 @@ public void readFields(DataInput in) throws IOException {
235240

236241
this.scan = Bytes.toString(Bytes.readByteArray(in));
237242
this.restoreDir = Bytes.toString(Bytes.readByteArray(in));
243+
try {
244+
this.length = in.readLong();
245+
} catch (EOFException e) {
246+
// Older serialized splits do not carry length and keep the previous behavior
247+
this.length = 0L;
248+
}
238249
}
239250
}
240251

@@ -441,6 +452,8 @@ public static List<InputSplit> getSplits(Scan scan, SnapshotManifest manifest,
441452
}
442453

443454
List<InputSplit> splits = new ArrayList<>();
455+
RegionSizes regionSizes =
456+
SnapshotRegionSizeCalculator.calculateRegionSizes(conf, manifest);
444457
for (HRegionInfo hri : regionManifests) {
445458
// load region descriptor
446459
List<String> hosts = null;
@@ -456,6 +469,8 @@ public static List<InputSplit> getSplits(Scan scan, SnapshotManifest manifest,
456469
}
457470
}
458471

472+
long snapshotRegionSize = regionSizes.getRegionSize(hri.getEncodedName());
473+
459474
if (numSplits > 1) {
460475
byte[][] sp = sa.split(hri.getStartKey(), hri.getEndKey(), numSplits, true);
461476
for (int i = 0; i < sp.length - 1; i++) {
@@ -478,7 +493,8 @@ public static List<InputSplit> getSplits(Scan scan, SnapshotManifest manifest,
478493
Bytes.compareTo(scan.getStopRow(), sp[i + 1]) < 0 ? scan.getStopRow() : sp[i + 1]);
479494
}
480495

481-
splits.add(new InputSplit(htd, hri, hosts, boundedScan, restoreDir));
496+
splits.add(new InputSplit(htd, hri, hosts, boundedScan, restoreDir,
497+
snapshotRegionSize / numSplits));
482498
}
483499
}
484500
} else {
@@ -487,7 +503,7 @@ public static List<InputSplit> getSplits(Scan scan, SnapshotManifest manifest,
487503
hri.getEndKey())
488504
) {
489505

490-
splits.add(new InputSplit(htd, hri, hosts, scan, restoreDir));
506+
splits.add(new InputSplit(htd, hri, hosts, scan, restoreDir, snapshotRegionSize));
491507
}
492508
}
493509
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hbase.snapshot;
19+
20+
import java.util.Collections;
21+
import java.util.Map;
22+
import org.apache.yetus.audience.InterfaceAudience;
23+
24+
/**
25+
* Holds the size of each region in a snapshot.
26+
*/
27+
@InterfaceAudience.Private
28+
public class RegionSizes {
29+
30+
private final Map<String, Long> regionSizeMap;
31+
32+
RegionSizes(Map<String, Long> regionSizeMap) {
33+
this.regionSizeMap = regionSizeMap;
34+
}
35+
36+
/**
37+
* Returns the total size of the given region in bytes.
38+
* @param encodedRegionName the encoded region name
39+
* @return the size of the region, or 0 if the region is not present in the snapshot
40+
*/
41+
public long getRegionSize(String encodedRegionName) {
42+
return regionSizeMap.getOrDefault(encodedRegionName, 0L);
43+
}
44+
45+
/**
46+
* Returns an unmodifiable view of the region size map.
47+
* @return a map of region encoded names to their total size in bytes
48+
*/
49+
public Map<String, Long> getRegionSizeMap() {
50+
return Collections.unmodifiableMap(regionSizeMap);
51+
}
52+
}

hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/SnapshotInfo.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ String getStateToString() {
160160
private AtomicLong hfilesMobSize = new AtomicLong();
161161
private AtomicLong nonSharedHfilesArchiveSize = new AtomicLong();
162162
private AtomicLong logSize = new AtomicLong();
163+
private Map<String, Long> regionSizeMap = new ConcurrentHashMap<>();
163164

164165
private final SnapshotProtos.SnapshotDescription snapshot;
165166
private final TableName snapshotTable;
@@ -182,6 +183,14 @@ String getStateToString() {
182183
this.fs = fs;
183184
}
184185

186+
/**
187+
* Returns the map containing region sizes.
188+
* @return A map where keys are region names and values are their corresponding sizes.
189+
*/
190+
public Map<String, Long> getRegionSizeMap() {
191+
return regionSizeMap;
192+
}
193+
185194
/** Returns the snapshot descriptor */
186195
public SnapshotDescription getSnapshotDescription() {
187196
return ProtobufUtil.createSnapshotDesc(this.snapshot);
@@ -353,6 +362,12 @@ FileInfo addStoreFile(final RegionInfo region, final String family,
353362
return new FileInfo(inArchive, size, isCorrupted);
354363
}
355364

365+
void updateRegionSizeMap(final RegionInfo region,
366+
final SnapshotRegionManifest.StoreFile storeFile) {
367+
long currentSize = regionSizeMap.getOrDefault(region.getEncodedName(), 0L);
368+
regionSizeMap.put(region.getEncodedName(), currentSize + storeFile.getFileSize());
369+
}
370+
356371
/**
357372
* Add the specified log file to the stats
358373
* @param server server name
@@ -633,6 +648,7 @@ public void storeFile(final RegionInfo regionInfo, final String family,
633648
final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
634649
if (!storeFile.hasReference()) {
635650
stats.addStoreFile(regionInfo, family, storeFile, filesMap);
651+
stats.updateRegionSizeMap(regionInfo, storeFile);
636652
}
637653
}
638654
});
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hbase.snapshot;
19+
20+
import java.io.IOException;
21+
import org.apache.hadoop.conf.Configuration;
22+
import org.apache.hadoop.fs.FileSystem;
23+
import org.apache.hadoop.hbase.snapshot.SnapshotInfo.SnapshotStats;
24+
import org.apache.hadoop.hbase.util.CommonFSUtils;
25+
import org.apache.yetus.audience.InterfaceAudience;
26+
27+
import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos;
28+
29+
/**
30+
* Utility class to calculate the size of each region in a snapshot.
31+
*/
32+
@InterfaceAudience.Private
33+
public class SnapshotRegionSizeCalculator {
34+
35+
/**
36+
* Calculate the size of each region in the snapshot.
37+
* @return A map of region encoded names to their total size in bytes.
38+
* @throws IOException If an error occurs during calculation.
39+
*/
40+
public static RegionSizes calculateRegionSizes(Configuration conf,
41+
SnapshotManifest manifest) throws IOException {
42+
FileSystem fs = FileSystem.get(CommonFSUtils.getRootDir(conf).toUri(), conf);
43+
SnapshotProtos.SnapshotDescription snapshot =
44+
SnapshotDescriptionUtils.readSnapshotInfo(fs, manifest.getSnapshotDir());
45+
SnapshotStats stats = SnapshotInfo.getSnapshotStats(conf, snapshot, null);
46+
return new RegionSizes(stats.getRegionSizeMap());
47+
}
48+
49+
}

0 commit comments

Comments
 (0)