|
38 | 38 |
|
39 | 39 | import java.io.IOException; |
40 | 40 | import java.util.ArrayList; |
| 41 | +import java.util.Arrays; |
41 | 42 | import java.util.HashMap; |
42 | 43 | import java.util.List; |
43 | 44 | import java.util.Map; |
44 | 45 | import java.util.UUID; |
45 | 46 | import java.util.stream.Collectors; |
| 47 | +import org.apache.hadoop.fs.FileStatus; |
| 48 | +import org.apache.hadoop.fs.FileSystem; |
| 49 | +import org.apache.hadoop.fs.Path; |
| 50 | +import org.apache.hadoop.hbase.HConstants; |
46 | 51 | import org.apache.hadoop.hbase.TableName; |
47 | 52 | import org.apache.hadoop.hbase.backup.BackupCopyJob; |
48 | 53 | import org.apache.hadoop.hbase.backup.BackupInfo; |
|
60 | 65 | import org.apache.hadoop.hbase.client.TableDescriptorBuilder; |
61 | 66 | import org.apache.hadoop.hbase.replication.ReplicationException; |
62 | 67 | import org.apache.hadoop.hbase.replication.ReplicationPeerConfig; |
| 68 | +import org.apache.hadoop.hbase.util.CommonFSUtils; |
63 | 69 | import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; |
| 70 | +import org.apache.hadoop.hbase.wal.AbstractFSWALProvider; |
64 | 71 | import org.apache.yetus.audience.InterfaceAudience; |
65 | 72 | import org.slf4j.Logger; |
66 | 73 | import org.slf4j.LoggerFactory; |
@@ -208,14 +215,17 @@ private void handleContinuousBackup(Admin admin) throws IOException { |
208 | 215 | } |
209 | 216 |
|
210 | 217 | private void handleNonContinuousBackup(Admin admin) throws IOException { |
| 218 | + Map<String, Long> previousLogRollsByHost = backupManager.readRegionServerLastLogRollResult(); |
211 | 219 | performLogRoll(); |
| 220 | + Map<String, Long> latestLogRollsByHost = newTimestamps; |
212 | 221 | performBackupSnapshots(admin); |
213 | 222 | backupManager.addIncrementalBackupTableSet(backupInfo.getTables()); |
214 | 223 |
|
215 | 224 | // set overall backup status: complete. Here we make sure to complete the backup. |
216 | 225 | // After this checkpoint, even if entering cancel process, will let the backup finished |
217 | 226 | backupInfo.setState(BackupState.COMPLETE); |
218 | 227 |
|
| 228 | + adjustTimestampsForOfflineHosts(previousLogRollsByHost, latestLogRollsByHost); |
219 | 229 | updateBackupMetadata(); |
220 | 230 | } |
221 | 231 |
|
@@ -248,6 +258,54 @@ private void performSnapshots(Admin admin) throws IOException { |
248 | 258 | } |
249 | 259 | } |
250 | 260 |
|
| 261 | + /** |
| 262 | + * Scan all WAL files to find offline/decommissioned hosts and record their max WAL timestamps in |
| 263 | + * newTimestamps. This ensures subsequent incremental backups won't re-include WALs already covered |
| 264 | + * by this full backup's snapshot. |
| 265 | + */ |
| 266 | + private void adjustTimestampsForOfflineHosts(Map<String, Long> previousLogRollsByHost, |
| 267 | + Map<String, Long> latestLogRollsByHost) throws IOException { |
| 268 | + Path walRootDir = CommonFSUtils.getWALRootDir(conf); |
| 269 | + Path logDir = new Path(walRootDir, HConstants.HREGION_LOGDIR_NAME); |
| 270 | + Path oldLogDir = new Path(walRootDir, HConstants.HREGION_OLDLOGDIR_NAME); |
| 271 | + FileSystem fs = walRootDir.getFileSystem(conf); |
| 272 | + |
| 273 | + List<FileStatus> allLogs = new ArrayList<>(); |
| 274 | + for (FileStatus hostLogDir : fs.listStatus(logDir)) { |
| 275 | + String host = BackupUtils.parseHostNameFromLogFile(hostLogDir.getPath()); |
| 276 | + if (host == null) { |
| 277 | + continue; |
| 278 | + } |
| 279 | + allLogs.addAll(Arrays.asList(fs.listStatus(hostLogDir.getPath()))); |
| 280 | + } |
| 281 | + allLogs.addAll(Arrays.asList(fs.listStatus(oldLogDir))); |
| 282 | + |
| 283 | + newTimestamps = new HashMap<>(); |
| 284 | + |
| 285 | + for (FileStatus log : allLogs) { |
| 286 | + if (AbstractFSWALProvider.isMetaFile(log.getPath())) { |
| 287 | + continue; |
| 288 | + } |
| 289 | + String host = BackupUtils.parseHostNameFromLogFile(log.getPath()); |
| 290 | + if (host == null) { |
| 291 | + continue; |
| 292 | + } |
| 293 | + long timestamp = BackupUtils.getCreationTime(log.getPath()); |
| 294 | + Long previousLogRoll = previousLogRollsByHost.get(host); |
| 295 | + Long latestLogRoll = latestLogRollsByHost.get(host); |
| 296 | + boolean isInactive = latestLogRoll == null || latestLogRoll.equals(previousLogRoll); |
| 297 | + |
| 298 | + if (isInactive) { |
| 299 | + long currentTs = newTimestamps.getOrDefault(host, 0L); |
| 300 | + if (timestamp > currentTs) { |
| 301 | + newTimestamps.put(host, timestamp); |
| 302 | + } |
| 303 | + } else { |
| 304 | + newTimestamps.put(host, latestLogRoll); |
| 305 | + } |
| 306 | + } |
| 307 | + } |
| 308 | + |
251 | 309 | private void updateBackupMetadata() throws IOException { |
252 | 310 | // The table list in backupInfo is good for both full backup and incremental backup. |
253 | 311 | // For incremental backup, it contains the incremental backup table set. |
|
0 commit comments