|
19 | 19 |
|
20 | 20 | package org.apache.iotdb.db.storageengine.load.active; |
21 | 21 |
|
| 22 | +import org.apache.iotdb.commons.utils.FileUtils; |
22 | 23 | import org.apache.iotdb.db.conf.IoTDBDescriptor; |
23 | 24 |
|
24 | 25 | import org.slf4j.Logger; |
25 | 26 | import org.slf4j.LoggerFactory; |
26 | 27 |
|
27 | 28 | import java.io.File; |
28 | | -import java.io.IOException; |
29 | | -import java.nio.file.FileVisitResult; |
30 | | -import java.nio.file.Files; |
31 | | -import java.nio.file.Path; |
32 | | -import java.nio.file.SimpleFileVisitor; |
33 | | -import java.nio.file.attribute.BasicFileAttributes; |
| 29 | +import java.util.ArrayList; |
34 | 30 | import java.util.Arrays; |
35 | | -import java.util.HashSet; |
36 | | -import java.util.Set; |
| 31 | +import java.util.List; |
37 | 32 |
|
38 | 33 | public class ActiveLoadAgent { |
39 | 34 |
|
@@ -72,101 +67,74 @@ public synchronized void start() { |
72 | 67 | * clean up all files and subdirectories in the listening directories, including: 1. Pending |
73 | 68 | * directories (configured by load_active_listening_dirs) 2. Pipe directory (for pipe data sync) |
74 | 69 | * 3. Failed directory (for failed files) |
| 70 | + * |
| 71 | + * <p>This method is called during DataNode startup and must not throw any exceptions to ensure |
| 72 | + * startup can proceed normally. All exceptions are caught and logged internally. |
75 | 73 | */ |
76 | 74 | public static void cleanupListeningDirectories() { |
77 | 75 | try { |
78 | | - final Set<String> dirsToClean = new HashSet<>(); |
79 | | - |
80 | | - try { |
81 | | - // Add configured listening dirs |
82 | | - if (IoTDBDescriptor.getInstance().getConfig().getLoadActiveListeningEnable()) { |
83 | | - dirsToClean.addAll( |
84 | | - Arrays.asList( |
85 | | - IoTDBDescriptor.getInstance().getConfig().getLoadActiveListeningDirs())); |
86 | | - } |
| 76 | + final List<String> dirsToClean = new ArrayList<>(); |
87 | 77 |
|
88 | | - // Add pipe dir |
89 | | - dirsToClean.add(IoTDBDescriptor.getInstance().getConfig().getLoadActiveListeningPipeDir()); |
| 78 | + dirsToClean.addAll( |
| 79 | + Arrays.asList(IoTDBDescriptor.getInstance().getConfig().getLoadActiveListeningDirs())); |
90 | 80 |
|
91 | | - // Add failed dir |
92 | | - dirsToClean.add(IoTDBDescriptor.getInstance().getConfig().getLoadActiveListeningFailDir()); |
93 | | - } catch (Exception e) { |
94 | | - LOGGER.warn("Failed to get active load listening directories configuration", e); |
95 | | - return; |
96 | | - } |
| 81 | + // Add pipe dir |
| 82 | + dirsToClean.add(IoTDBDescriptor.getInstance().getConfig().getLoadActiveListeningPipeDir()); |
97 | 83 |
|
98 | | - int totalFilesDeleted = 0; |
99 | | - int totalSubDirsDeleted = 0; |
| 84 | + // Add failed dir |
| 85 | + dirsToClean.add(IoTDBDescriptor.getInstance().getConfig().getLoadActiveListeningFailDir()); |
100 | 86 |
|
| 87 | + // Clean up each directory |
101 | 88 | for (final String dirPath : dirsToClean) { |
102 | 89 | try { |
| 90 | + if (dirPath == null || dirPath.isEmpty()) { |
| 91 | + continue; |
| 92 | + } |
| 93 | + |
103 | 94 | final File dir = new File(dirPath); |
104 | 95 |
|
105 | | - if (!dir.exists() || !dir.isDirectory()) { |
| 96 | + // Check if directory exists and is a directory |
| 97 | + // These methods may throw SecurityException if access is denied |
| 98 | + try { |
| 99 | + if (!dir.exists() || !dir.isDirectory()) { |
| 100 | + continue; |
| 101 | + } |
| 102 | + } catch (Exception e) { |
| 103 | + LOGGER.debug("Failed to check directory: {}", dirPath, e); |
106 | 104 | continue; |
107 | 105 | } |
108 | 106 |
|
109 | | - // Convert to absolute path for comparison |
110 | | - final String absoluteDirPath = dir.getAbsolutePath(); |
111 | | - |
112 | | - final long[] fileCount = {0}; |
113 | | - final long[] subdirCount = {0}; |
114 | | - |
115 | | - Files.walkFileTree( |
116 | | - dir.toPath(), |
117 | | - new SimpleFileVisitor<Path>() { |
118 | | - @Override |
119 | | - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { |
120 | | - try { |
121 | | - Files.delete(file); |
122 | | - fileCount[0]++; |
123 | | - } catch (Exception e) { |
124 | | - LOGGER.debug("Failed to delete file: {}", file.toAbsolutePath(), e); |
125 | | - } |
126 | | - return FileVisitResult.CONTINUE; |
127 | | - } |
128 | | - |
129 | | - @Override |
130 | | - public FileVisitResult postVisitDirectory(Path subDir, IOException exc) { |
131 | | - if (exc != null) { |
132 | | - LOGGER.debug( |
133 | | - "Error occurred while visiting directory: {}", |
134 | | - subDir.toAbsolutePath(), |
135 | | - exc); |
136 | | - return FileVisitResult.CONTINUE; |
137 | | - } |
138 | | - if (!subDir.toFile().getAbsolutePath().equals(absoluteDirPath)) { |
139 | | - try { |
140 | | - Files.delete(subDir); |
141 | | - subdirCount[0]++; |
142 | | - } catch (Exception e) { |
143 | | - LOGGER.debug("Failed to delete directory: {}", subDir.toAbsolutePath(), e); |
144 | | - } |
145 | | - } |
146 | | - return FileVisitResult.CONTINUE; |
147 | | - } |
148 | | - |
149 | | - @Override |
150 | | - public FileVisitResult visitFileFailed(Path file, IOException exc) { |
151 | | - LOGGER.debug("Failed to visit file: {}", file.toAbsolutePath(), exc); |
152 | | - return FileVisitResult.CONTINUE; |
153 | | - } |
154 | | - }); |
155 | | - |
156 | | - totalFilesDeleted += fileCount[0]; |
157 | | - totalSubDirsDeleted += subdirCount[0]; |
| 107 | + // Only delete contents inside the directory, not the directory itself |
| 108 | + // listFiles() may throw SecurityException if access is denied |
| 109 | + File[] files = null; |
| 110 | + try { |
| 111 | + files = dir.listFiles(); |
| 112 | + } catch (Exception e) { |
| 113 | + LOGGER.warn("Failed to list files in directory: {}", dirPath, e); |
| 114 | + continue; |
| 115 | + } |
| 116 | + |
| 117 | + if (files != null) { |
| 118 | + for (final File file : files) { |
| 119 | + // FileUtils.deleteFileOrDirectory internally calls file.isDirectory() and |
| 120 | + // file.listFiles() without try-catch, so exceptions may propagate here. |
| 121 | + // We need to catch it to prevent one file failure from stopping the cleanup. |
| 122 | + try { |
| 123 | + FileUtils.deleteFileOrDirectory(file, true); |
| 124 | + } catch (Exception e) { |
| 125 | + LOGGER.debug("Failed to delete file or directory: {}", file.getAbsolutePath(), e); |
| 126 | + } |
| 127 | + } |
| 128 | + } |
158 | 129 | } catch (Exception e) { |
159 | 130 | LOGGER.warn("Failed to cleanup directory: {}", dirPath, e); |
160 | 131 | } |
161 | 132 | } |
162 | 133 |
|
163 | | - if (totalFilesDeleted > 0 || totalSubDirsDeleted > 0) { |
164 | | - LOGGER.info( |
165 | | - "Cleaned up active load listening directories, deleted {} files and {} subdirectories", |
166 | | - totalFilesDeleted, |
167 | | - totalSubDirsDeleted); |
168 | | - } |
| 134 | + LOGGER.info("Cleaned up active load listening directories"); |
169 | 135 | } catch (Throwable t) { |
| 136 | + // Catch all exceptions and errors (including OutOfMemoryError, etc.) |
| 137 | + // to ensure startup process is not affected |
170 | 138 | LOGGER.warn("Unexpected error during cleanup of active load listening directories", t); |
171 | 139 | } |
172 | 140 | } |
|
0 commit comments