Skip to content

Commit

Permalink
Merge pull request #54 from levigo/feat/JF-756-AddJS5TimedLoadTest
Browse files Browse the repository at this point in the history
feat(JF-756): Add JS5 Timed Load Test
  • Loading branch information
hoffmadd authored Feb 14, 2024
2 parents 686eebe + f5cc87a commit cf0a44f
Show file tree
Hide file tree
Showing 8 changed files with 300 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,46 @@
import java.util.concurrent.TimeUnit;

public class JobCardScheduler {

private final static JobCardScheduler INSTANCE = new JobCardScheduler();

private static final class MyShutdownHook extends Thread {

public MyShutdownHook() {
super("ShutdownHook for JobCardScheduler");
}

@Override
public void run() {
getInstance().executor.shutdown();
getInstance().executor.shutdownNow();
}
}

public static JobCardScheduler getInstance() {
return INSTANCE;
}

private JobCardScheduler() {
Runtime.getRuntime().addShutdownHook(new MyShutdownHook());
Preferences.concurrentJobsProperty().addListener((obj, oldValue, newValue) -> {
executor.setCorePoolSize(newValue.intValue());
});
executor.setCorePoolSize(Preferences.concurrentJobsProperty().getValue());
}

private ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 4,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());

public void submit(JobCard card) {
executor.submit(card);
}

public void shutdown() {
this.executor.shutdown();
}

private final static JobCardScheduler INSTANCE = new JobCardScheduler();

private static final class MyShutdownHook extends Thread {

public MyShutdownHook() {
super("ShutdownHook for JobCardScheduler");
}

@Override
public void run() {
getInstance().executor.shutdown();
getInstance().executor.shutdownNow();
}
}

public static JobCardScheduler getInstance() {
return INSTANCE;
}

private JobCardScheduler() {
Runtime.getRuntime().addShutdownHook(new MyShutdownHook());
Preferences.concurrentJobsProperty().addListener((obj, oldValue, newValue) -> {
executor.setCorePoolSize(newValue.intValue());
});
executor.setCorePoolSize(Preferences.concurrentJobsProperty().getValue());
}

private ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 4, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());

public void submit(JobCard card) {
executor.submit(card);
}

public void shutdown() {
this.executor.shutdown();
}

public int getCurrentQueueSize() {
return executor.getQueue().size();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;

import org.apache.log4j.Logger;
Expand All @@ -15,48 +16,51 @@
import com.levigo.jadice.server.shared.types.Stream;

public class FilenameGenerator {

private static final Logger LOGGER = Logger.getLogger(FilenameGenerator.class);

private static Analyzer analyzer;

public final static String DEFAULT_PATTERN = PatternKeys.ORIGINAL_FILENAME + "-" + PatternKeys.NUMMER + "."
+ PatternKeys.EXTENSION;
private static Analyzer analyzer;

public final static String DEFAULT_PATTERN = PatternKeys.ORIGINAL_FILENAME + "-" + PatternKeys.NUMMER + "-"
+ PatternKeys.UUID + "." + PatternKeys.EXTENSION;

public static interface PatternKeys {
final String JOB_ID = "%j";
final String ORIGINAL_FILENAME = "%f";
final String EXTENSION = "%e";
final String NUMMER = "%n";
final String TIMESTAMP = "%t";
final String UUID = "%u";
final String PERCENT_SIGN = "%%";
}

public static String generateFilename(Job job, Stream stream, File originalFile, int nmbr) {
String pttrn = Preferences.resultFilenamePatternProperty().getValue();
// Caveat! Matcher.quoteReplacement(...) in order to escape $ and \ signs

if (mustDetermineExtension()) {
final String extension = determineExtension(stream);
pttrn = pttrn.replaceAll(PatternKeys.EXTENSION, Matcher.quoteReplacement(extension));
}
return pttrn.replaceAll(PatternKeys.JOB_ID, Matcher.quoteReplacement(job.getUUID()))//
.replaceAll(PatternKeys.ORIGINAL_FILENAME, Matcher.quoteReplacement(originalFile.getName()))//
.replaceAll(PatternKeys.NUMMER, Integer.toString(nmbr))//
.replaceAll(PatternKeys.UUID, UUID.randomUUID().toString().replace("-", ""))//
.replaceAll(PatternKeys.TIMESTAMP, Long.toString(System.currentTimeMillis()))//
.replaceAll(PatternKeys.PERCENT_SIGN, "%")
// Paranoia checks to prevent from storing in other folders:
.replaceAll("/", "")//
.replace("\\", "");
}

protected static boolean mustDetermineExtension() {
return Preferences.resultFilenamePatternProperty().getValue().contains(PatternKeys.EXTENSION);
}

private static String determineExtension(Stream stream) {
try {
final UncloseableSeekableInputStreamWrapper usis = new UncloseableSeekableInputStreamWrapper(stream.getInputStream());
final UncloseableSeekableInputStreamWrapper usis = new UncloseableSeekableInputStreamWrapper(
stream.getInputStream());
final Map<String, Object> alResults;
try {
usis.seek(0);
Expand Down Expand Up @@ -103,6 +107,8 @@ public static String buildExplanationText(String sep) {
s += FilenameGenerator.PatternKeys.TIMESTAMP + ": timestamp";
s += sep;
s += FilenameGenerator.PatternKeys.PERCENT_SIGN + ": %";
s += sep;
s += FilenameGenerator.PatternKeys.UUID + ": generated UUID";
return s;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package org.levigo.jadice.server.converterclient;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import org.junit.Before;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.levigo.jadice.server.converterclient.configurations.WorkflowConfiguration;

public class JS5TimedLoadTest {

private long testDurationMS = TimeUnit.MINUTES.toMillis(15);

private String templateID = "x2tiff (DOCP)";
private File testdataFolder = new File(System.getProperty("user.dir"), "/testdata/perf-test/image");

// private String templateID = "mail2pdf (lo)";
// private File testdataFolder = new File(System.getProperty("user.dir"),
// "/testdata/perf-test/email");

private int targetQueueSize = 40;

private JobCardScheduler scheduler = JobCardScheduler.getInstance();

private long sleepTimeBetweenCheckMS = 1000;

private List<File> testdataImageFiles = new ArrayList<>();
private int currentTestdataIndex = 0;

private List<JobCard> createdJobCards = new ArrayList<>();
private int currentItemCounter = 0;

private String serverLocation = "tcp://localhost:61616";
private WorkflowConfiguration config;

@Disabled // TODO: Enable on your local machine
@Test
public void performLoadTest() throws Exception {
init();

long totalDurationMS = System.currentTimeMillis();
config = JobCardFactory.getInstance().getConfiguration(templateID);
if (config == null) {
throw new IllegalArgumentException("Configuration \"" + templateID + "\" unknown");
}

Map<String, Object> info = new HashMap<>();
info.put("desiredRuntime [sec]", (testDurationMS / 1000));
info.put("templateID", templateID);
info.put("targetQueueSize", targetQueueSize);
info.put("startTime", new Date(System.currentTimeMillis()));

System.out.println("Performing load test...\n" + info);

doPerformTest();

totalDurationMS = System.currentTimeMillis() - totalDurationMS;

// Add some statistics
info.put("createdItemCount", currentItemCounter);
info.put("createdJobCount", createdJobCards.size());

double timePerJob = Double.valueOf(totalDurationMS).doubleValue()
/ Double.valueOf(createdJobCards.size()).doubleValue();
double timePerItem = Double.valueOf(totalDurationMS).doubleValue()
/ Double.valueOf(currentItemCounter).doubleValue();
double jobsPerMinute = Double.valueOf(60000) / timePerJob;
double itemsPerMinute = Double.valueOf(60000) / timePerItem;
double itemsPerHour = itemsPerMinute * 60;

info.put("timePerItem", timePerItem);
info.put("itemsPerMinute", itemsPerMinute);
info.put("itemsPerHour", itemsPerHour);
info.put("timePerJob", timePerJob);
info.put("jobsPerMinute", jobsPerMinute);

// Log finished message
StringBuilder sb = new StringBuilder();
for (String key : info.keySet()) {
if (sb.length() > 0) {
sb.append(System.lineSeparator());
}
sb.append(key);
sb.append("=");
sb.append(info.get(key));
}

String msg = String.format("Finished load test:\n===========================\n%s\n===========================",
sb.toString());

System.out.println(msg);

System.out.println("Test ended");
}

private void init() {
System.out.println("Reading testdata folder...");
// Read all file objects from folder so we can easily choose later on via rotating index
testdataImageFiles.clear();
if (testdataFolder.exists()) {
File[] sub = testdataFolder.listFiles();
if (null != sub) {
for (File s : sub) {
testdataImageFiles.add(s);
}
}
}
}

@Before
private void beforeEachTest() {
currentItemCounter = 0;
createdJobCards.clear();
currentTestdataIndex = 0;
}

private void doPerformTest() throws Exception {
boolean shallRun = true;
long start = System.currentTimeMillis();
long end = start + testDurationMS;

System.out.println("Running performance test. Will run until: " + new Date(end));

while (shallRun) {
int currentQueueSize = scheduler.getCurrentQueueSize();

if (currentQueueSize < targetQueueSize) {
System.out.println("Creating new jobs to fill up queue...");

while (currentQueueSize < targetQueueSize) {

System.out.println("Creating next job");
JobCard jobCard = createNextJob();
createdJobCards.add(jobCard);

currentItemCounter++;

currentQueueSize = scheduler.getCurrentQueueSize();
}
}

if (System.currentTimeMillis() > end) {
shallRun = false;
} else {
Thread.sleep(sleepTimeBetweenCheckMS);
System.out.println(String.format(
"Running... Created job count: %s / created item count %s / runtime left %s min", createdJobCards.size(),
currentItemCounter, Duration.ofMillis(end - System.currentTimeMillis()).toMinutes()));
}
}

System.out.println("Loop finished");
}

private JobCard createNextJob() throws Exception {

if (currentTestdataIndex > testdataImageFiles.size() - 2) {
currentTestdataIndex = 0;
} else {
currentTestdataIndex++;
}

File chosenFile = testdataImageFiles.get(currentTestdataIndex);

JobCard jobCard = JobCardFactory.getInstance().createAndSubmitJobCard(chosenFile, serverLocation, config,
Collections.emptySet());

return jobCard;
}

private void processResult(JobCard jobCard) throws IOException, InterruptedException {
List<File> result = new ArrayList<File>();

jobCard.job.waitForTermination(-1, TimeUnit.SECONDS);

while (!jobCard.isResultsCompleted()) {
Thread.sleep(100);
}

for (File from : jobCard.getResults()) {
String outFilename = "./target/out/" + /* inFile.getName() + */ "-out-" + UUID.randomUUID().toString() + "-"
+ from.getName();
File outFile = new File(System.getProperty("user.dir"), outFilename);

copyFile(from, outFile);
result.add(outFile);
}
}

public static void copyFile(File from, File to) throws IOException {
final FileInputStream fis = new FileInputStream(from);
try {
final FileOutputStream fos = new FileOutputStream(to);
try {
final FileChannel inChannel = fis.getChannel();
final FileChannel outChannel = fos.getChannel();
inChannel.transferTo(0, inChannel.size(), outChannel);
} finally {
fos.close();
}
} finally {
fis.close();
}
}
}
Loading

0 comments on commit cf0a44f

Please sign in to comment.