This is an automated email from the ASF dual-hosted git repository.
jt2594838 pushed a commit to branch dev/1.3
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/dev/1.3 by this push:
new fd0f76bbcb2 [To dev/1.3] Fix Load task cleanup and input handling
(#18936)
fd0f76bbcb2 is described below
commit fd0f76bbcb22766c7d7797fc968c26f51cb3ead5
Author: Caideyipi <[email protected]>
AuthorDate: Tue Oct 20 18:39:27 2026 +0800
[To dev/1.3] Fix Load task cleanup and input handling (#18936)
Port the improvements from PR #18772 to the legacy Load implementation.
Adapt the active loader tests and cover rollback while nested Load work remains
active.
---
.../iotdb/db/storageengine/StorageEngine.java | 43 +++---
.../db/storageengine/load/LoadTsFileManager.java | 126 +++++++++++++-----
.../load/LoadTsFilePieceNodeAssembler.java | 2 +-
.../load/active/ActiveLoadDirScanner.java | 10 ++
.../storageengine/load/LoadTsFileManagerTest.java | 145 +++++++++++++++++++++
.../load/LoadTsFilePieceNodeAssemblerTest.java | 12 ++
.../load/active/ActiveLoadDirScannerTest.java | 30 +++++
7 files changed, 312 insertions(+), 56 deletions(-)
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java
index becd7404a56..b3179e8556e 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java
@@ -999,26 +999,31 @@ public class StorageEngine implements IService {
final int sliceIndex,
final int sliceCount,
final int originBodySize) {
- final LoadTsFilePieceNodeAssembler.Result result =
- loadTsFileManager.appendPieceNodeSlice(
- dataRegionId, uuid, body, sliceIndex, sliceCount, originBodySize);
- if (!result.isValid()) {
- return RpcUtils.getStatus(
- TSStatusCode.DESERIALIZE_PIECE_OF_TSFILE_ERROR,
result.getErrorMessage());
- }
- if (!result.isComplete()) {
- return RpcUtils.SUCCESS_STATUS;
- }
+ return loadTsFileManager.executeLoadTask(
+ uuid,
+ () -> {
+ final LoadTsFilePieceNodeAssembler.Result result =
+ loadTsFileManager.appendPieceNodeSlice(
+ dataRegionId, uuid, body, sliceIndex, sliceCount,
originBodySize);
+ if (!result.isValid()) {
+ return RpcUtils.getStatus(
+ TSStatusCode.DESERIALIZE_PIECE_OF_TSFILE_ERROR,
result.getErrorMessage());
+ }
+ if (!result.isComplete()) {
+ return RpcUtils.SUCCESS_STATUS;
+ }
- try {
- final Object planNode = PlanNodeType.deserialize(result.getBody());
- if (!(planNode instanceof LoadTsFilePieceNode)) {
- return new
TSStatus(TSStatusCode.DESERIALIZE_PIECE_OF_TSFILE_ERROR.getStatusCode());
- }
- return writeLoadTsFileNode(dataRegionId, (LoadTsFilePieceNode) planNode,
uuid);
- } catch (final Exception e) {
- return new
TSStatus(TSStatusCode.DESERIALIZE_PIECE_OF_TSFILE_ERROR.getStatusCode());
- }
+ try {
+ final Object planNode = PlanNodeType.deserialize(result.getBody());
+ if (!(planNode instanceof LoadTsFilePieceNode)) {
+ return new
TSStatus(TSStatusCode.DESERIALIZE_PIECE_OF_TSFILE_ERROR.getStatusCode());
+ }
+ return writeLoadTsFileNode(dataRegionId, (LoadTsFilePieceNode)
planNode, uuid);
+ } catch (final Exception e) {
+ return new
TSStatus(TSStatusCode.DESERIALIZE_PIECE_OF_TSFILE_ERROR.getStatusCode());
+ }
+ },
+ new TSStatus(TSStatusCode.LOAD_FILE_ERROR.getStatusCode()));
}
public TSStatus executeLoadCommand(
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/LoadTsFileManager.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/LoadTsFileManager.java
index abbce7022a3..b99808f2696 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/LoadTsFileManager.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/LoadTsFileManager.java
@@ -82,12 +82,12 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -163,6 +163,57 @@ public class LoadTsFileManager {
}
}
+ private CleanupTask beginLoadTask(final String uuid) {
+ synchronized (uuid2CleanupTask) {
+ createCleanupTaskIfAbsent(uuid);
+ final CleanupTask cleanupTask = uuid2CleanupTask.get(uuid);
+ if (cleanupTask.isCleanupRequested || cleanupTask.isCanceled) {
+ return null;
+ }
+
+ cleanupTask.runningTaskCount++;
+ cleanupTask.isLoadTaskRunning = true;
+ rescheduleCleanupTask(cleanupTask);
+ return cleanupTask;
+ }
+ }
+
+ private void finishLoadTask(final CleanupTask cleanupTask) {
+ finishLoadTask(cleanupTask, false);
+ }
+
+ private void finishLoadTask(final CleanupTask cleanupTask, final boolean
cleanupAfter) {
+ synchronized (uuid2CleanupTask) {
+ if (uuid2CleanupTask.get(cleanupTask.uuid) != cleanupTask) {
+ return;
+ }
+
+ cleanupTask.isCleanupRequested |= cleanupAfter;
+ cleanupTask.runningTaskCount--;
+ cleanupTask.isLoadTaskRunning = cleanupTask.runningTaskCount > 0;
+
+ if (!cleanupTask.isLoadTaskRunning && cleanupTask.isCleanupRequested) {
+ clean(cleanupTask.uuid);
+ } else {
+ rescheduleCleanupTask(cleanupTask);
+ }
+ }
+ }
+
+ public <T> T executeLoadTask(
+ final String uuid, final Supplier<T> operation, final T rejectedResult) {
+ final CleanupTask cleanupTask = beginLoadTask(uuid);
+ if (cleanupTask == null) {
+ return rejectedResult;
+ }
+
+ try {
+ return operation.get();
+ } finally {
+ finishLoadTask(cleanupTask);
+ }
+ }
+
private void rescheduleCleanupTask(final CleanupTask cleanupTask) {
synchronized (uuid2CleanupTask) {
if (uuid2CleanupTask.get(cleanupTask.uuid) != cleanupTask) {
@@ -252,10 +303,10 @@ public class LoadTsFileManager {
public void writeToDataRegion(DataRegion dataRegion, LoadTsFilePieceNode
pieceNode, String uuid)
throws IOException, PageException {
- createCleanupTaskIfAbsent(uuid);
-
- final Optional<CleanupTask> cleanupTask =
Optional.ofNullable(uuid2CleanupTask.get(uuid));
- cleanupTask.ifPresent(CleanupTask::markLoadTaskRunning);
+ final CleanupTask cleanupTask = beginLoadTask(uuid);
+ if (cleanupTask == null) {
+ throw new
IOException(String.format(MESSAGE_WRITER_MANAGER_HAS_BEEN_CLOSED, uuid));
+ }
try {
final AtomicReference<Exception> exception = new AtomicReference<>();
final TsFileWriterManager writerManager =
@@ -287,7 +338,7 @@ public class LoadTsFileManager {
}
}
} finally {
- cleanupTask.ifPresent(CleanupTask::markLoadTaskNotRunning);
+ finishLoadTask(cleanupTask);
}
}
@@ -298,10 +349,11 @@ public class LoadTsFileManager {
final int sliceIndex,
final int sliceCount,
final int originBodySize) {
- createCleanupTaskIfAbsent(uuid);
-
- final Optional<CleanupTask> cleanupTask =
Optional.ofNullable(uuid2CleanupTask.get(uuid));
- cleanupTask.ifPresent(CleanupTask::markLoadTaskRunning);
+ final CleanupTask cleanupTask = beginLoadTask(uuid);
+ if (cleanupTask == null) {
+ return LoadTsFilePieceNodeAssembler.Result.invalid(
+ String.format(MESSAGE_WRITER_MANAGER_HAS_BEEN_CLOSED, uuid));
+ }
try {
final Map<DataRegionId, LoadTsFilePieceNodeAssembler> regionId2Assembler
=
uuid2PieceNodeAssembler.computeIfAbsent(uuid, key -> new
ConcurrentHashMap<>());
@@ -330,7 +382,7 @@ public class LoadTsFileManager {
return result;
}
} finally {
- cleanupTask.ifPresent(CleanupTask::markLoadTaskNotRunning);
+ finishLoadTask(cleanupTask);
}
}
@@ -373,32 +425,42 @@ public class LoadTsFileManager {
boolean isGeneratedByPipe,
Map<TTimePartitionSlot, ProgressIndex> timePartitionProgressIndexMap)
throws IOException, LoadFileException {
- if (!uuid2WriterManager.containsKey(uuid) ||
uuid2PieceNodeAssembler.containsKey(uuid)) {
+ final CleanupTask cleanupTask = beginLoadTask(uuid);
+ if (cleanupTask == null) {
return false;
}
- createCleanupTaskIfAbsent(uuid);
-
- final Optional<CleanupTask> cleanupTask =
Optional.ofNullable(uuid2CleanupTask.get(uuid));
- cleanupTask.ifPresent(CleanupTask::markLoadTaskRunning);
+ boolean loaded = false;
try {
+ if (!uuid2WriterManager.containsKey(uuid) ||
uuid2PieceNodeAssembler.containsKey(uuid)) {
+ return false;
+ }
uuid2WriterManager.get(uuid).loadAll(isGeneratedByPipe,
timePartitionProgressIndexMap);
+ loaded = true;
+ return true;
} finally {
- cleanupTask.ifPresent(CleanupTask::markLoadTaskNotRunning);
+ finishLoadTask(cleanupTask, loaded);
}
-
- clean(uuid);
- return true;
}
public boolean deleteAll(String uuid) {
- if (!uuid2WriterManager.containsKey(uuid)
- && !uuid2PieceNodeAssembler.containsKey(uuid)
- && !uuid2CleanupTask.containsKey(uuid)) {
- return false;
+ synchronized (uuid2CleanupTask) {
+ if (!uuid2WriterManager.containsKey(uuid)
+ && !uuid2PieceNodeAssembler.containsKey(uuid)
+ && !uuid2CleanupTask.containsKey(uuid)) {
+ return false;
+ }
+
+ final CleanupTask cleanupTask = uuid2CleanupTask.get(uuid);
+ if (cleanupTask != null && cleanupTask.isLoadTaskRunning) {
+ cleanupTask.isCleanupRequested = true;
+ rescheduleCleanupTask(cleanupTask);
+ return true;
+ }
+
+ clean(uuid);
+ return true;
}
- clean(uuid);
- return true;
}
private void clean(String uuid) {
@@ -824,6 +886,8 @@ public class LoadTsFileManager {
private long scheduledTime;
private volatile boolean isLoadTaskRunning = false;
+ private int runningTaskCount;
+ private boolean isCleanupRequested;
private volatile boolean isCanceled = false;
private CleanupTask(String uuid, long delayInMs) {
@@ -832,16 +896,6 @@ public class LoadTsFileManager {
resetScheduledTime();
}
- public void markLoadTaskRunning() {
- isLoadTaskRunning = true;
- rescheduleCleanupTask(this);
- }
-
- public void markLoadTaskNotRunning() {
- isLoadTaskRunning = false;
- rescheduleCleanupTask(this);
- }
-
public void resetScheduledTime() {
scheduledTime = System.currentTimeMillis() + delayInMs;
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/LoadTsFilePieceNodeAssembler.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/LoadTsFilePieceNodeAssembler.java
index ccf9d77ac8f..b35ecafd20f 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/LoadTsFilePieceNodeAssembler.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/LoadTsFilePieceNodeAssembler.java
@@ -34,7 +34,7 @@ public final class LoadTsFilePieceNodeAssembler {
LoadTsFilePieceNodeAssembler(final int sliceCount, final int originBodySize)
{
this.sliceCount = sliceCount;
this.originBodySize = originBodySize;
- this.assembledBody = new PublicBAOS(Math.max(0, originBodySize));
+ this.assembledBody = new PublicBAOS();
}
synchronized Result append(
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/active/ActiveLoadDirScanner.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/active/ActiveLoadDirScanner.java
index c07c0d8e61a..68735c9e386 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/active/ActiveLoadDirScanner.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/active/ActiveLoadDirScanner.java
@@ -108,6 +108,7 @@ public class ActiveLoadDirScanner extends
ActiveLoadScheduledExecutorService {
FileUtils.streamFiles(listeningDirFile, true, (String[]) null)) {
try {
fileStream
+ .filter(file -> !isUnderFailDir(file))
.filter(file ->
!ActiveLoadPathHelper.isTransferStagingFile(file, listeningDirFile))
.map(file -> new File(getTsFilePath(file.getAbsolutePath())))
.distinct()
@@ -142,6 +143,15 @@ public class ActiveLoadDirScanner extends
ActiveLoadScheduledExecutorService {
}
}
+ private boolean isUnderFailDir(final File file) {
+ final String failDir = IOTDB_CONFIG.getLoadActiveListeningFailDir();
+ if (failDir == null || failDir.isEmpty()) {
+ return false;
+ }
+ final Path failDirPath = new
File(failDir).toPath().toAbsolutePath().normalize();
+ return file.toPath().toAbsolutePath().normalize().startsWith(failDirPath);
+ }
+
private boolean checkPermission(final String listeningDir) {
try {
final Path listeningDirPath = new File(listeningDir).toPath();
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/load/LoadTsFileManagerTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/load/LoadTsFileManagerTest.java
new file mode 100644
index 00000000000..9a2a9e6ee9c
--- /dev/null
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/load/LoadTsFileManagerTest.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.storageengine.load;
+
+import org.apache.iotdb.db.conf.IoTDBConfig;
+import org.apache.iotdb.db.conf.IoTDBDescriptor;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+public class LoadTsFileManagerTest {
+
+ private final IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig();
+ private String[][] originalTierDataDirs;
+ private File tempDir;
+ private LoadTsFileManager loadTsFileManager;
+
+ @Before
+ public void setUp() throws Exception {
+ originalTierDataDirs = copyDirs(config.getTierDataDirs());
+ tempDir = Files.createTempDirectory("load-tsfile-manager").toFile();
+ config.setTierDataDirs(new String[][] {{tempDir.getAbsolutePath()}});
+ loadTsFileManager = new LoadTsFileManager();
+ }
+
+ @After
+ public void tearDown() {
+ if (loadTsFileManager != null) {
+ loadTsFileManager.stop();
+ }
+ config.setTierDataDirs(originalTierDataDirs);
+ deleteRecursively(tempDir);
+ }
+
+ @Test
+ public void testRollbackDefersCleanupUntilActiveLoadTaskFinishes() throws
Exception {
+ final String uuid = "rollback-test";
+ final CountDownLatch taskStarted = new CountDownLatch(1);
+ final CountDownLatch finishTask = new CountDownLatch(1);
+ final ExecutorService executor = Executors.newSingleThreadExecutor();
+ try {
+ final Future<String> activeTask =
+ executor.submit(
+ () ->
+ loadTsFileManager.executeLoadTask(
+ uuid,
+ () -> {
+ taskStarted.countDown();
+ try {
+ if (!finishTask.await(10, TimeUnit.SECONDS)) {
+ throw new AssertionError("Timed out waiting to
finish the load task");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
+ return "finished";
+ },
+ "rejected"));
+
+ Assert.assertTrue(taskStarted.await(10, TimeUnit.SECONDS));
+ Assert.assertTrue(loadTsFileManager.deleteAll(uuid));
+ Assert.assertEquals(
+ "rejected", loadTsFileManager.executeLoadTask(uuid, () ->
"unexpected", "rejected"));
+
+ finishTask.countDown();
+ Assert.assertEquals("finished", activeTask.get(10, TimeUnit.SECONDS));
+ Assert.assertFalse(loadTsFileManager.deleteAll(uuid));
+ } finally {
+ finishTask.countDown();
+ executor.shutdownNow();
+ }
+ }
+
+ @Test
+ public void testRollbackWaitsForOuterLoadTaskAfterNestedTaskFinishes() {
+ final String uuid = "nested-rollback-test";
+ Assert.assertEquals(
+ "finished",
+ loadTsFileManager.executeLoadTask(
+ uuid,
+ () -> {
+ Assert.assertEquals(
+ "nested",
+ loadTsFileManager.executeLoadTask(
+ uuid,
+ () -> {
+ Assert.assertTrue(loadTsFileManager.deleteAll(uuid));
+ return "nested";
+ },
+ "rejected"));
+ Assert.assertEquals(
+ "rejected",
+ loadTsFileManager.executeLoadTask(uuid, () -> "unexpected",
"rejected"));
+ return "finished";
+ },
+ "rejected"));
+ Assert.assertFalse(loadTsFileManager.deleteAll(uuid));
+ }
+
+ private static String[][] copyDirs(final String[][] dirs) {
+ return Arrays.stream(dirs).map(String[]::clone).toArray(String[][]::new);
+ }
+
+ private static void deleteRecursively(final File file) {
+ if (file == null || !file.exists()) {
+ return;
+ }
+ final File[] children = file.listFiles();
+ if (children != null) {
+ for (final File child : children) {
+ deleteRecursively(child);
+ }
+ }
+ Assert.assertTrue(file.delete());
+ }
+}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/load/LoadTsFilePieceNodeAssemblerTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/load/LoadTsFilePieceNodeAssemblerTest.java
index 79c572a14ed..74ca74cdb11 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/load/LoadTsFilePieceNodeAssemblerTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/load/LoadTsFilePieceNodeAssemblerTest.java
@@ -133,6 +133,18 @@ public class LoadTsFilePieceNodeAssemblerTest {
}
}
+ @Test
+ public void testLargeDeclaredBodySizeDoesNotPreallocateTheWholeBody() {
+ final LoadTsFilePieceNodeAssembler assembler =
+ new LoadTsFilePieceNodeAssembler(2, Integer.MAX_VALUE);
+
+ final LoadTsFilePieceNodeAssembler.Result result =
+ assembler.append(ByteBuffer.wrap(new byte[] {1}), 0, 2,
Integer.MAX_VALUE);
+
+ Assert.assertTrue(result.isValid());
+ Assert.assertFalse(result.isComplete());
+ }
+
@Test
public void testRejectBodySizeViolationsWithReason() {
final LoadTsFilePieceNodeAssembler.Result overflow =
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/load/active/ActiveLoadDirScannerTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/load/active/ActiveLoadDirScannerTest.java
index 5703ee0ec55..1eb0594eb33 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/load/active/ActiveLoadDirScannerTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/load/active/ActiveLoadDirScannerTest.java
@@ -45,6 +45,7 @@ public class ActiveLoadDirScannerTest {
private final IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig();
private String[] originalListeningDirs;
private String originalPipeDir;
+ private String originalFailDir;
private boolean originalListeningEnabled;
private File tempDir;
private File pendingDir;
@@ -54,6 +55,7 @@ public class ActiveLoadDirScannerTest {
public void setUp() throws Exception {
originalListeningDirs = config.getLoadActiveListeningDirs();
originalPipeDir = config.getLoadActiveListeningPipeDir();
+ originalFailDir = config.getLoadActiveListeningFailDir();
originalListeningEnabled = config.getLoadActiveListeningEnable();
tempDir = Files.createTempDirectory("active-load-scanner").toFile();
@@ -70,6 +72,7 @@ public class ActiveLoadDirScannerTest {
public void tearDown() {
config.setLoadActiveListeningDirs(originalListeningDirs);
config.setLoadActiveListeningPipeDir(originalPipeDir);
+ config.setLoadActiveListeningFailDir(originalFailDir);
config.setLoadActiveListeningEnable(originalListeningEnabled);
ActiveLoadUtil.updateLoadDiskSelector();
deleteRecursively(tempDir);
@@ -121,6 +124,33 @@ public class ActiveLoadDirScannerTest {
eq(tsFile.getAbsolutePath()), eq(pendingDir.getAbsolutePath()),
eq(false));
}
+ @Test
+ public void testScanIgnoresFailDirectoryNestedUnderListeningDirectory()
throws Exception {
+ final File failDir = new File(pendingDir, "failed");
+ Assert.assertTrue(failDir.mkdirs());
+ config.setLoadActiveListeningFailDir(failDir.getAbsolutePath());
+ final File failedTsFile = createCompletedTsFile(failDir, "failed.tsfile");
+ final File pendingTsFile = createCompletedTsFile(pendingDir,
"pending.tsfile");
+
+ final ActiveLoadTsFileLoader loader = mock(ActiveLoadTsFileLoader.class);
+ when(loader.getCurrentAllowedPendingSize()).thenReturn(10);
+ final ActiveLoadDirScanner scanner = new ActiveLoadDirScanner(loader);
+ try {
+ final Method scanMethod =
ActiveLoadDirScanner.class.getDeclaredMethod("scan");
+ scanMethod.setAccessible(true);
+ scanMethod.invoke(scanner);
+ } finally {
+ scanner.stop();
+ }
+
+ verify(loader, times(1))
+ .tryTriggerTsFileLoad(
+ eq(pendingTsFile.getAbsolutePath()),
eq(pendingDir.getAbsolutePath()), eq(false));
+ verify(loader, times(0))
+ .tryTriggerTsFileLoad(
+ eq(failedTsFile.getAbsolutePath()),
eq(pendingDir.getAbsolutePath()), eq(false));
+ }
+
private static File createCompletedTsFile(final File dir, final String
fileName)
throws Exception {
final File tsFile = new File(dir, fileName);