✅ chore: Add concurrent update file download status test
This commit is contained in:
parent
56997142d6
commit
b4ace30cc1
4 changed files with 234 additions and 0 deletions
|
|
@ -5,6 +5,7 @@ APP_ENV=prod
|
|||
APP_ROOT=/app/data
|
||||
# only use in test
|
||||
#TDLIB_PATH=
|
||||
#JAVA_HOME=
|
||||
|
||||
TELEGRAM_API_ID=
|
||||
TELEGRAM_API_HASH=
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package telegram.files;
|
||||
|
||||
import io.vertx.core.Future;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.nio.MappedByteBuffer;
|
||||
|
|
@ -61,4 +63,16 @@ public class MessyUtils {
|
|||
|
||||
return md5File1.equals(md5File2);
|
||||
}
|
||||
|
||||
public static <T> T await(Future<T> future) {
|
||||
CompletableFuture<T> completableFuture = new CompletableFuture<>();
|
||||
future.onComplete(result -> {
|
||||
if (result.succeeded()) {
|
||||
completableFuture.complete(result.result());
|
||||
} else {
|
||||
completableFuture.completeExceptionally(result.cause());
|
||||
}
|
||||
});
|
||||
return completableFuture.join();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,175 @@
|
|||
package telegram.files;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.junit.jupiter.api.parallel.Execution;
|
||||
import org.junit.jupiter.api.parallel.ExecutionMode;
|
||||
import telegram.files.repository.FileRecord;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@Execution(ExecutionMode.CONCURRENT)
|
||||
public class FileDownloadStatusConcurrentTest {
|
||||
|
||||
private static final int THREAD_COUNT = 10;
|
||||
|
||||
private static ExecutorService executor;
|
||||
|
||||
static FileRecord fileRecord = new FileRecord(
|
||||
1, "unique_id", 1, 1, 1, 1, false, 1, 0, "type", "mime_type", "file_name", "thumbnail", "caption", null,
|
||||
FileRecord.DownloadStatus.idle.name(), FileRecord.TransferStatus.idle.name(), 0, null
|
||||
);
|
||||
|
||||
@BeforeAll
|
||||
static void setUpAll() {
|
||||
executor = Executors.newFixedThreadPool(THREAD_COUNT);
|
||||
|
||||
Vertx vertx = Vertx.vertx();
|
||||
Future<String> future = vertx.deployVerticle(new DataVerticle());
|
||||
MessyUtils.await(future);
|
||||
|
||||
Future<FileRecord> fileRecordFuture = DataVerticle.fileRepository.create(fileRecord);
|
||||
MessyUtils.await(fileRecordFuture);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void tearDownAll() {
|
||||
executor.shutdown();
|
||||
String dataPath = DataVerticle.getDataPath();
|
||||
if (FileUtil.file(dataPath).exists()) {
|
||||
FileUtil.del(dataPath);
|
||||
}
|
||||
}
|
||||
|
||||
@RepeatedTest(THREAD_COUNT)
|
||||
@DisplayName("Concurrent update file download status")
|
||||
void concurrentUpdateFileDownloadStatusTest() {
|
||||
int newFileId = 2;
|
||||
|
||||
String updateLocalPath = "local_path_" + Thread.currentThread().threadId();
|
||||
Long completionDate = System.currentTimeMillis();
|
||||
|
||||
Future<JsonObject> future = DataVerticle.fileRepository.updateDownloadStatus(newFileId, fileRecord.uniqueId(), updateLocalPath, FileRecord.DownloadStatus.downloading, completionDate);
|
||||
MessyUtils.await(future);
|
||||
|
||||
Future<FileRecord> finalRecordFuture = DataVerticle.fileRepository.getByPrimaryKey(newFileId, fileRecord.uniqueId());
|
||||
FileRecord finalRecord = MessyUtils.await(finalRecordFuture);
|
||||
|
||||
Assertions.assertEquals(FileRecord.DownloadStatus.downloading.name(), finalRecord.downloadStatus());
|
||||
Assertions.assertNotNull(finalRecord.localPath());
|
||||
Assertions.assertNotNull(finalRecord.completionDate());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Multi-threaded concurrent update file download status using ExecutorService")
|
||||
void multiThreadedConcurrentUpdateFileDownloadStatusTest() throws Exception {
|
||||
int newFileId = 2;
|
||||
|
||||
List<java.util.concurrent.Future<Void>> futures = new ArrayList<>();
|
||||
AtomicInteger completedCount = new AtomicInteger(0);
|
||||
|
||||
for (int i = 0; i < THREAD_COUNT; i++) {
|
||||
int threadIndex = i;
|
||||
futures.add(executor.submit(() -> {
|
||||
String updateLocalPath = "local_path_" + threadIndex;
|
||||
Long completionDate = System.currentTimeMillis();
|
||||
System.out.println("Thread start update: " + Thread.currentThread().getName() + ", localPath: " + updateLocalPath + ", completionDate: " + completionDate);
|
||||
|
||||
try {
|
||||
DataVerticle.fileRepository.updateDownloadStatus(newFileId,
|
||||
fileRecord.uniqueId(),
|
||||
updateLocalPath,
|
||||
FileRecord.DownloadStatus.downloading,
|
||||
completionDate)
|
||||
.onComplete(result -> completedCount.incrementAndGet());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return null;
|
||||
}));
|
||||
}
|
||||
|
||||
for (int i = 0; i < THREAD_COUNT; i++) {
|
||||
futures.add(executor.submit(() -> {
|
||||
try {
|
||||
DataVerticle.fileRepository.getByUniqueId(fileRecord.uniqueId());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return null;
|
||||
}));
|
||||
}
|
||||
|
||||
for (java.util.concurrent.Future<Void> future : futures) {
|
||||
future.get();
|
||||
}
|
||||
while (completedCount.get() < THREAD_COUNT) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
|
||||
Future<FileRecord> finalRecordFuture = DataVerticle.fileRepository.getByPrimaryKey(newFileId, fileRecord.uniqueId());
|
||||
FileRecord finalRecord = MessyUtils.await(finalRecordFuture);
|
||||
|
||||
Assertions.assertEquals(FileRecord.DownloadStatus.downloading.name(), finalRecord.downloadStatus());
|
||||
Assertions.assertNotNull(finalRecord.localPath());
|
||||
Assertions.assertNotNull(finalRecord.completionDate());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Multi-process concurrent update file download status")
|
||||
void multiProcessConcurrentUpdateFileDownloadStatusTest() throws Exception {
|
||||
int newFileId = 2;
|
||||
|
||||
int processCount = 5;
|
||||
List<Process> processes = new ArrayList<>();
|
||||
String classpath = System.getProperty("java.class.path");
|
||||
String javaPath = System.getProperty("java.home") + "/bin/java";
|
||||
|
||||
for (int i = 0; i < processCount; i++) {
|
||||
String localPath = "local_path_" + i;
|
||||
ProcessBuilder pb = new ProcessBuilder(javaPath,
|
||||
"-cp",
|
||||
classpath,
|
||||
"-Djava.library.path=" + System.getenv("TDLIB_PATH"),
|
||||
"telegram.files.UpdateFileDownloadProcess",
|
||||
String.valueOf(newFileId),
|
||||
"unique_id",
|
||||
localPath,
|
||||
"downloading",
|
||||
"1");
|
||||
pb.environment().putAll(System.getenv());
|
||||
pb.inheritIO();
|
||||
processes.add(pb.start());
|
||||
}
|
||||
|
||||
for (Process process : processes) {
|
||||
process.waitFor();
|
||||
}
|
||||
|
||||
Future<FileRecord> future = DataVerticle.fileRepository.getByPrimaryKey(newFileId, fileRecord.uniqueId());
|
||||
FileRecord finalRecord = MessyUtils.await(future);
|
||||
|
||||
Assertions.assertNotNull(finalRecord, "Final record should not be null");
|
||||
Assertions.assertTrue(
|
||||
finalRecord.downloadStatus().equals(FileRecord.DownloadStatus.downloading.name()) ||
|
||||
finalRecord.downloadStatus().equals(FileRecord.DownloadStatus.completed.name()),
|
||||
"Download status should be one of the concurrent updates"
|
||||
);
|
||||
Assertions.assertTrue(
|
||||
IntStream.range(0, processCount).anyMatch(i -> finalRecord.localPath().equals("local_path_" + i)),
|
||||
"Local path should match one of the concurrent updates"
|
||||
);
|
||||
Assertions.assertTrue(
|
||||
finalRecord.completionDate().equals(1L) || finalRecord.completionDate().equals(2L),
|
||||
"Completion date should match one of the concurrent updates"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package telegram.files;
|
||||
|
||||
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import telegram.files.repository.FileRecord;
|
||||
|
||||
public class UpdateFileDownloadProcess {
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length < 5) {
|
||||
System.out.println("Usage: UpdateFileDownloadProcess <fileId> <uniqueId> <localPath> <downloadStatus> <completionDate>");
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.println("Process: " + ProcessHandle.current().pid() + " started");
|
||||
|
||||
int fileId = Integer.parseInt(args[0]);
|
||||
String uniqueId = args[1];
|
||||
String localPath = args[2];
|
||||
String downloadStatus = args[3];
|
||||
Long completionDate = Long.parseLong(args[4]);
|
||||
|
||||
try {
|
||||
Vertx vertx = Vertx.vertx();
|
||||
Future<String> deployVerticle = vertx.deployVerticle(new DataVerticle());
|
||||
MessyUtils.await(deployVerticle);
|
||||
|
||||
Future<JsonObject> future = DataVerticle.fileRepository.updateDownloadStatus(fileId,
|
||||
uniqueId,
|
||||
localPath,
|
||||
FileRecord.DownloadStatus.valueOf(downloadStatus),
|
||||
completionDate);
|
||||
MessyUtils.await(future);
|
||||
System.out.println("Process: " + ProcessHandle.current().pid() + " finished");
|
||||
System.exit(0);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Process: " + ProcessHandle.current().pid() + " failed");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue