Skip to content

Commit

Permalink
Fix performance impact of max memory usage check
Browse files Browse the repository at this point in the history
  • Loading branch information
jacodg committed Dec 18, 2023
1 parent 59065c6 commit 83c9207
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 18 deletions.
1 change: 1 addition & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Ladybug Test Tool release notes
Upcoming (2.3)
--------------

- Fix performance impact of max memory usage check
- Add maxStorageSize to DatabaseStorage
- Add HideMessageTransformer and LinkingMessageTransformer
- Add run result info to Debug tab
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/nl/nn/testtool/Checkpoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ public long getEstimatedMemoryUsage() {
if (message == null) {
return 0L;
} else {
return message.length() * 2;
return message.length() * 2L;
}
}

Expand Down
43 changes: 26 additions & 17 deletions src/main/java/nl/nn/testtool/Report.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@ public class Report implements Serializable {
private List<Checkpoint> checkpoints = new ArrayList<Checkpoint>();
private String transformation;
private String variableCsv;
// Please note that the get and set methods need @Transient annotation for
// XmlEncoder to not store the property. This is in contrast to
// serialization / ObjectOutputStream that is using variables (and doesn't
// look at get and set methods) and needs a variable to be declared
// transient to not store the field.
// Please note that the get and set methods need @Transient annotation for XmlEncoder to not store the property.
// This is in contrast to serialization / ObjectOutputStream that is using variables (and doesn't look at get and
// set methods) and needs a variable to be declared transient to not store the field.
// Also note that ObjectInputStream will use default values 0 and false for transient primitives, see
// https://stackoverflow.com/questions/10531076/serialization-via-objectinputstream-and-transient-fields
private transient String mainThread;
private transient long mainThreadFinishedTime = TIME_NOT_SET_VALUE;
private transient List<String> threads = new ArrayList<String>();
Expand Down Expand Up @@ -111,6 +111,9 @@ public class Report implements Serializable {
// When using CXF see jsonProvider in cxf-beans.xml
private transient Integer transientStorageId;
private transient long storageSize;
// When set to a different value then 0 it will still be 0 after being initialized by ObjectInputStream, see
// comment above about default values for ObjectInputStream
private transient long estimatedMemoryUsage = 0L;
private transient ReportXmlTransformer reportXmlTransformer;
private transient ReportXmlTransformer globalReportXmlTransformer;
private transient String xml;
Expand All @@ -125,15 +128,6 @@ public class Report implements Serializable {
private transient Map<Object, Set<Checkpoint>> streamingMessageListeners = new HashMap<Object, Set<Checkpoint>>();
private transient Map<Object, StreamingMessageResult> streamingMessageResults = new HashMap<Object, StreamingMessageResult>();

public Report() {
mainThread = Thread.currentThread().getName();
threads.add(mainThread);
threadCheckpointIndex.put(mainThread, 0);
threadFirstLevel.put(mainThread, 0);
threadLevel.put(mainThread, 0);
threadsActiveCount++;
}

@Transient
@JsonIgnore
public void setTestTool(TestTool testTool) {
Expand Down Expand Up @@ -315,6 +309,15 @@ public boolean isReportFilterMatching() {
return reportFilterMatching;
}

protected void init() {
mainThread = Thread.currentThread().getName();
threads.add(mainThread);
threadCheckpointIndex.put(mainThread, 0);
threadFirstLevel.put(mainThread, 0);
threadLevel.put(mainThread, 0);
threadsActiveCount++;
}

protected <T> T checkpoint(String childThreadId, String sourceClassName, String name, T message,
StubableCode stubableCode, StubableCodeThrowsException stubableCodeThrowsException,
Set<String> matchingStubStrategies, int checkpointType, int levelChangeNextCheckpoint) {
Expand Down Expand Up @@ -572,6 +575,7 @@ private <T> T addCheckpoint(String threadName, String sourceClassName, String n
Integer value = threadCheckpointIndex.get(key);
threadCheckpointIndex.put(key, value + 1);
}
estimatedMemoryUsage += checkpoint.getEstimatedMemoryUsage();
if (log.isDebugEnabled()) {
log.debug("Added checkpoint " + getCheckpointLogDescription(name, checkpointType, level));
}
Expand Down Expand Up @@ -795,9 +799,14 @@ public int getNumberOfCheckpoints() {
}

public long getEstimatedMemoryUsage() {
long estimatedMemoryUsage = 0L;
for (Checkpoint checkpoint : checkpoints) {
estimatedMemoryUsage += checkpoint.getEstimatedMemoryUsage();
// Variable estimatedMemoryUsage is transient so it needs to be recalculated after the Report object has been
// loaded from storage. In other situations it should not be recalculated because this method is being called
// every time a checkpoint is added (to check whether the max memory usage has been exceeded) so recalculating
// it every time would be bad for performance.
if (estimatedMemoryUsage == 0L && mainThread == null) {
for (Checkpoint checkpoint : checkpoints) {
estimatedMemoryUsage += checkpoint.getEstimatedMemoryUsage();
}
}
return estimatedMemoryUsage;
}
Expand Down
1 change: 1 addition & 0 deletions src/main/java/nl/nn/testtool/TestTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ private Report getReportInProgress(String correlationId, String name, int checkp
report.setStubStrategy(originalReport.getStubStrategy());
report.setOriginalReport(originalReport);
}
report.init();
reportsInProgress.add(0, report);
reportsInProgressByCorrelationId.put(correlationId, report);
numberOfReportsInProgress++;
Expand Down

0 comments on commit 83c9207

Please sign in to comment.