|
27 | 27 | import org.apache.fluss.exception.InvalidColumnProjectionException; |
28 | 28 | import org.apache.fluss.exception.InvalidCoordinatorException; |
29 | 29 | import org.apache.fluss.exception.InvalidRequiredAcksException; |
| 30 | +import org.apache.fluss.exception.KvStorageException; |
30 | 31 | import org.apache.fluss.exception.LogOffsetOutOfRangeException; |
31 | 32 | import org.apache.fluss.exception.LogStorageException; |
32 | 33 | import org.apache.fluss.exception.NotLeaderOrFollowerException; |
|
124 | 125 |
|
125 | 126 | import java.io.File; |
126 | 127 | import java.io.IOException; |
| 128 | +import java.nio.file.DirectoryNotEmptyException; |
127 | 129 | import java.nio.file.Files; |
| 130 | +import java.nio.file.NoSuchFileException; |
128 | 131 | import java.nio.file.Path; |
129 | 132 | import java.util.ArrayList; |
130 | 133 | import java.util.Collections; |
|
145 | 148 |
|
146 | 149 | import static org.apache.fluss.config.ConfigOptions.KV_FORMAT_VERSION_2; |
147 | 150 | import static org.apache.fluss.server.TabletManagerBase.getTableInfo; |
148 | | -import static org.apache.fluss.utils.FileUtils.isDirectoryEmpty; |
149 | 151 | import static org.apache.fluss.utils.Preconditions.checkArgument; |
150 | 152 | import static org.apache.fluss.utils.Preconditions.checkNotNull; |
151 | 153 | import static org.apache.fluss.utils.Preconditions.checkState; |
@@ -978,7 +980,20 @@ public void stopReplicas( |
978 | 980 | TableBucket tb = data.getTableBucket(); |
979 | 981 | HostedReplica hostedReplica = getReplica(tb); |
980 | 982 | if (hostedReplica instanceof NoneReplica) { |
981 | | - // do nothing fort this case. |
| 983 | + if (data.isDeleteLocal()) { |
| 984 | + try { |
| 985 | + sweepOrphanTabletDirs(tb, deletedTableIds, deletedPartitionIds); |
| 986 | + } catch (Exception e) { |
| 987 | + LOG.error( |
| 988 | + "Failed to sweep orphan tablet directories for {}", |
| 989 | + tb, |
| 990 | + e); |
| 991 | + result.add( |
| 992 | + new StopReplicaResultForBucket( |
| 993 | + tb, ApiError.fromThrowable(e))); |
| 994 | + continue; |
| 995 | + } |
| 996 | + } |
982 | 997 | result.add(new StopReplicaResultForBucket(tb)); |
983 | 998 | } else if (hostedReplica instanceof OfflineReplica) { |
984 | 999 | LOG.warn( |
@@ -1927,6 +1942,59 @@ private StopReplicaResultForBucket stopReplica( |
1927 | 1942 | return new StopReplicaResultForBucket(tb); |
1928 | 1943 | } |
1929 | 1944 |
|
| 1945 | + /** |
| 1946 | + * Remove on-disk tablet directories for a bucket that the in-memory ReplicaManager does not |
| 1947 | + * know about. This handles the case where a stopReplica(delete=true) arrives after the |
| 1948 | + * TabletServer was restarted during a delete — LogManager loaded the log at startup but no |
| 1949 | + * NotifyLeaderAndIsr ever ran, so allReplicas is empty. |
| 1950 | + */ |
| 1951 | + private void sweepOrphanTabletDirs( |
| 1952 | + TableBucket tb, Map<Long, Path> deletedTableIds, Map<Long, Path> deletedPartitionIds) { |
| 1953 | + Optional<LogTablet> orphanLog = logManager.getLog(tb); |
| 1954 | + if (!orphanLog.isPresent()) { |
| 1955 | + return; |
| 1956 | + } |
| 1957 | + |
| 1958 | + LogTablet logTablet = orphanLog.get(); |
| 1959 | + File dataDir = logTablet.getDataDir(); |
| 1960 | + PhysicalTablePath physicalTablePath = logTablet.getPhysicalTablePath(); |
| 1961 | + Path tabletParentDir = logManager.getTabletParentDir(dataDir, physicalTablePath, tb); |
| 1962 | + |
| 1963 | + // Clean KV before log so that if KV cleanup fails, the log is still |
| 1964 | + // present and a coordinator retry can re-enter this method. |
| 1965 | + boolean isKvTable = false; |
| 1966 | + if (kvManager.getKv(tb).isPresent()) { |
| 1967 | + kvManager.dropKv(tb); |
| 1968 | + isKvTable = true; |
| 1969 | + } else { |
| 1970 | + File kvTabletDir = FlussPaths.kvTabletDir(dataDir, physicalTablePath, tb); |
| 1971 | + if (kvTabletDir.exists()) { |
| 1972 | + isKvTable = true; |
| 1973 | + try { |
| 1974 | + FileUtils.deleteDirectory(kvTabletDir); |
| 1975 | + } catch (IOException e) { |
| 1976 | + throw new KvStorageException( |
| 1977 | + String.format( |
| 1978 | + "Failed to delete orphan KV tablet directory %s", kvTabletDir), |
| 1979 | + e); |
| 1980 | + } |
| 1981 | + } |
| 1982 | + } |
| 1983 | + |
| 1984 | + logManager.dropLog(tb); |
| 1985 | + |
| 1986 | + localDiskManager.recordReplicaDelete(dataDir, isKvTable); |
| 1987 | + |
| 1988 | + if (tb.getPartitionId() != null) { |
| 1989 | + deletedPartitionIds.put(tb.getPartitionId(), tabletParentDir); |
| 1990 | + deletedTableIds.put(tb.getTableId(), tabletParentDir.getParent()); |
| 1991 | + } else { |
| 1992 | + deletedTableIds.put(tb.getTableId(), tabletParentDir); |
| 1993 | + } |
| 1994 | + |
| 1995 | + LOG.info("Swept orphan tablet directories for bucket {}", tb); |
| 1996 | + } |
| 1997 | + |
1930 | 1998 | private void truncateToHighWatermark(List<Replica> replicas) { |
1931 | 1999 | for (Replica replica : replicas) { |
1932 | 2000 | long highWatermark = replica.getLogTablet().getHighWatermark(); |
@@ -1960,14 +2028,19 @@ private void validateAndApplyCoordinatorEpoch(int requestCoordinatorEpoch, Strin |
1960 | 2028 | } |
1961 | 2029 |
|
1962 | 2030 | private void dropEmptyTableOrPartitionDir(Path dir, long id, String dirType) { |
1963 | | - if (!Files.exists(dir) || !isDirectoryEmpty(dir)) { |
1964 | | - return; |
1965 | | - } |
1966 | | - |
1967 | | - LOG.info("Drop empty {} dir '{}' of {} id {}.", dirType, dir, dirType, id); |
1968 | 2031 | try { |
1969 | | - FileUtils.deleteDirectory(dir.toFile()); |
1970 | | - } catch (Exception e) { |
| 2032 | + Files.delete(dir); |
| 2033 | + LOG.info("Dropped empty {} dir '{}' of {} id {}.", dirType, dir, dirType, id); |
| 2034 | + } catch (DirectoryNotEmptyException e) { |
| 2035 | + LOG.warn( |
| 2036 | + "{} dir '{}' of {} id {} is not empty, skipping deletion.", |
| 2037 | + dirType, |
| 2038 | + dir, |
| 2039 | + dirType, |
| 2040 | + id); |
| 2041 | + } catch (NoSuchFileException ignored) { |
| 2042 | + // Already gone — fine. |
| 2043 | + } catch (IOException e) { |
1971 | 2044 | LOG.error("Failed to delete empty {} dir '{}' of {} id {}.", dirType, dir, dirType, e); |
1972 | 2045 | } |
1973 | 2046 | } |
|
0 commit comments