Skip to content

Commit 9d997d9

Browse files
Merge pull request #364 from commercetools/246-improve-b
246 improve benchmarks
2 parents e10c822 + bc79d5f commit 9d997d9

8 files changed

Lines changed: 129 additions & 223 deletions

File tree

.travis.yml

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,8 @@ before_install:
1313
install: true # skips travis' default installation step which executes gradle assemble.
1414
jobs:
1515
include:
16-
- stage: benchmark#1
17-
if: tag IS present
18-
script: ./gradlew clean setLibraryVersion benchmark benchmarkCommit
19-
- stage: benchmark#2
20-
if: tag IS present
21-
script: ./gradlew clean setLibraryVersion benchmark benchmarkCommit
22-
- stage: benchmark#3
23-
if: tag IS present
24-
script: ./gradlew clean setLibraryVersion benchmark benchmarkCommit
25-
- stage: benchmark#4
26-
if: tag IS present
27-
script: ./gradlew clean setLibraryVersion benchmark benchmarkCommit
28-
- stage: benchmark#5
29-
if: tag IS present
16+
- stage: benchmark
17+
if: branch = master AND commit_message =~ /^(Merge pull request .*)/
3018
script: ./gradlew clean setLibraryVersion benchmark benchmarkCommit
3119
- stage: full build
3220
script: ./gradlew clean dependencyUpdates setLibraryVersion build

docs/RELEASE_NOTES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@
4747
now treat the values of the optional fields `isSearchable`, `inputHint` and `attributeConstraint`
4848
as (`true`, `SingleLine` and `None` respectivley) if they are `null` or not passed. [#354](https://github.com/commercetools/commercetools-sync-java/issues/354)
4949
50+
- 🛠️ **Enhancements** (1)
51+
- **Commons** - Benchmarks are now run once on every merge to `master` with a lower number of resources for faster benchmarking. [#246](https://github.com/commercetools/commercetools-sync-java/issues/246)
52+
5053
- 📋 **Documentation** (2)
5154
- **Commons** - Added link to [documentation pages](https://commercetools.github.io/commercetools-sync-java) in README of the github repo.
5255
- **Commons** - Fixed link of [`beforeUpdateCallback` for keeping other variants](https://github.com/commercetools/commercetools-sync-java/tree/master/src/integration-test/java/com/commercetools/sync/integration/ctpprojectsource/products/templates/beforeupdatecallback/KeepOtherVariantsSyncIT.java) example in the [Sync Options](/docs/usage/SYNC_OPTIONS.md) doc page. [#360](https://github.com/commercetools/commercetools-sync-java/issues/360)

src/benchmark/java/com/commercetools/sync/benchmark/BenchmarkUtils.java

Lines changed: 51 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,18 @@
1111
import java.nio.charset.StandardCharsets;
1212
import java.nio.file.Files;
1313
import java.nio.file.Paths;
14-
import java.util.Iterator;
15-
import java.util.List;
16-
import java.util.Optional;
17-
import java.util.Spliterator;
18-
import java.util.stream.Collectors;
19-
import java.util.stream.Stream;
2014

2115
import static java.util.Optional.ofNullable;
22-
import static java.util.Spliterators.spliteratorUnknownSize;
23-
import static java.util.stream.StreamSupport.stream;
2416

25-
public class BenchmarkUtils {
17+
final class BenchmarkUtils {
2618
private static final String BENCHMARK_RESULTS_FILE_NAME = "benchmarks.json";
2719
private static final String BENCHMARK_RESULTS_FILE_DIR = ofNullable(System.getenv("CI_BUILD_DIR"))
2820
.map(path -> path + "/tmp_git_dir/benchmarks/").orElse("");
2921
private static final String BENCHMARK_RESULTS_FILE_PATH = BENCHMARK_RESULTS_FILE_DIR + BENCHMARK_RESULTS_FILE_NAME;
3022
private static final Charset UTF8_CHARSET = StandardCharsets.UTF_8;
31-
private static final String EXECUTION_TIMES = "executionTimes";
32-
private static final String AVERAGE = "average";
33-
private static final String DIFF = "diff";
23+
private static final String EXECUTION_TIME = "executionTime";
24+
private static final String BRANCH_NAME = ofNullable(System.getenv("TRAVIS_COMMIT"))
25+
.orElse("dev-local");
3426

3527
static final String PRODUCT_SYNC = "productSync";
3628
static final String INVENTORY_SYNC = "inventorySync";
@@ -40,137 +32,86 @@ public class BenchmarkUtils {
4032
static final String CREATES_ONLY = "createsOnly";
4133
static final String UPDATES_ONLY = "updatesOnly";
4234
static final String CREATES_AND_UPDATES = "mix";
43-
static final int THRESHOLD = 120000; //120 seconds in milliseconds
44-
static final int NUMBER_OF_RESOURCE_UNDER_TEST = 10000;
35+
static final int NUMBER_OF_RESOURCE_UNDER_TEST = 1000;
36+
static final String THRESHOLD_EXCEEDED_ERROR = "Total execution time of benchmark '%d' took longer than allowed"
37+
+ " threshold of '%d'.";
4538

4639

47-
static void saveNewResult(@Nonnull final String version,
48-
@Nonnull final String sync,
49-
@Nonnull final String benchmark,
50-
final double newResult) throws IOException {
40+
static void saveNewResult(@Nonnull final String sync, @Nonnull final String benchmark, final double newResult)
41+
throws IOException {
5142

52-
final JsonNode rootNode = new ObjectMapper().readTree(getFileContent(BENCHMARK_RESULTS_FILE_PATH));
53-
final JsonNode withNewResult = addNewResult(rootNode, version, sync, benchmark, newResult);
54-
writeToFile(withNewResult.toString(), BENCHMARK_RESULTS_FILE_PATH);
43+
final JsonNode rootNode = new ObjectMapper().readTree(getFileContent());
44+
final JsonNode withNewResult = addNewResult(rootNode, sync, benchmark, newResult);
45+
writeToFile(withNewResult.toString());
5546
}
5647

48+
@Nonnull
49+
private static String getFileContent() throws IOException {
50+
51+
final byte[] fileBytes = Files.readAllBytes(Paths.get(BENCHMARK_RESULTS_FILE_PATH));
52+
return new String(fileBytes, UTF8_CHARSET);
53+
}
54+
55+
@Nonnull
5756
private static JsonNode addNewResult(@Nonnull final JsonNode originalRoot,
58-
@Nonnull final String version,
5957
@Nonnull final String sync,
6058
@Nonnull final String benchmark,
6159
final double newResult) {
60+
6261
ObjectNode rootNode = (ObjectNode) originalRoot;
63-
ObjectNode versionNode = (ObjectNode) rootNode.get(version);
62+
ObjectNode branchNode = (ObjectNode) rootNode.get(BRANCH_NAME);
6463

6564
// If version doesn't exist yet, create a new JSON object for the new version.
66-
if (versionNode == null) {
67-
rootNode = createVersionNode(rootNode, version);
68-
versionNode = (ObjectNode) rootNode.get(version);
65+
if (branchNode == null) {
66+
branchNode = createVersionNode();
67+
rootNode.set(BRANCH_NAME, branchNode);
6968
}
7069

71-
final ObjectNode syncNode = (ObjectNode) versionNode.get(sync);
70+
final ObjectNode syncNode = (ObjectNode) branchNode.get(sync);
7271
final ObjectNode benchmarkNode = (ObjectNode) syncNode.get(benchmark);
7372

74-
// Get current list of execution times for the specified benchmark of the specified sync module
75-
// of the specified version.
76-
final List<JsonNode> results = toList(benchmarkNode.get(EXECUTION_TIMES).elements());
77-
7873
// Add new result.
79-
results.add(JsonNodeFactory.instance.numberNode(newResult));
80-
benchmarkNode.set(EXECUTION_TIMES, JsonNodeFactory.instance.arrayNode().addAll(results));
81-
82-
// Compute new average and add to JSON Object
83-
final double averageResult = calculateAvg(results);
84-
benchmarkNode.set(AVERAGE, JsonNodeFactory.instance.numberNode(averageResult));
85-
86-
// Compute new diff from the last version.
87-
final double diff = calculateDiff(rootNode, version, sync, benchmark, averageResult);
88-
benchmarkNode.set(DIFF, JsonNodeFactory.instance.numberNode(diff));
89-
90-
final JsonNode newSyncNode = syncNode.set(benchmark, benchmarkNode);
91-
final JsonNode newVersionNode = versionNode.set(sync, newSyncNode);
92-
final JsonNode newRoot = rootNode.set(version, newVersionNode);
93-
return newRoot;
74+
benchmarkNode.set(EXECUTION_TIME, JsonNodeFactory.instance.numberNode(newResult));
75+
return rootNode;
9476
}
9577

9678
@Nonnull
97-
private static <T> List<T> toList(@Nonnull final Iterator<T> iterator) {
98-
return toStream(iterator).collect(Collectors.toList());
99-
}
79+
private static ObjectNode createVersionNode() {
10080

101-
@Nonnull
102-
private static <T> Stream<T> toStream(@Nonnull final Iterator<T> iterator) {
103-
return stream(spliteratorUnknownSize(iterator, Spliterator.ORDERED), false);
104-
}
105-
106-
private static double calculateAvg(@Nonnull final List<JsonNode> results) {
107-
return results.stream().mapToDouble(JsonNode::asLong).average().orElse(0);
108-
}
81+
final ObjectNode newVersionNode = JsonNodeFactory.instance.objectNode();
82+
newVersionNode.set(PRODUCT_SYNC, createSyncNode());
83+
newVersionNode.set(INVENTORY_SYNC, createSyncNode());
84+
newVersionNode.set(CATEGORY_SYNC, createSyncNode());
85+
newVersionNode.set(PRODUCT_TYPE_SYNC, createSyncNode());
86+
newVersionNode.set(TYPE_SYNC, createSyncNode());
10987

110-
private static ObjectNode createVersionNode(@Nonnull final ObjectNode rootNode, @Nonnull final String version) {
111-
final ObjectNode newVersionNode = createSyncNode(createSyncNode(createSyncNode(
112-
createSyncNode(createSyncNode(JsonNodeFactory.instance.objectNode(),
113-
PRODUCT_SYNC), INVENTORY_SYNC), CATEGORY_SYNC), PRODUCT_TYPE_SYNC), TYPE_SYNC);
114-
return (ObjectNode) rootNode.set(version, newVersionNode);
88+
return newVersionNode;
11589
}
11690

117-
private static ObjectNode createSyncNode(@Nonnull final ObjectNode versionNode,
118-
@Nonnull final String sync) {
119-
final ObjectNode newSyncNode = createBenchmarkNode(
120-
createBenchmarkNode(
121-
createBenchmarkNode(JsonNodeFactory.instance.objectNode(), CREATES_ONLY), UPDATES_ONLY),
122-
CREATES_AND_UPDATES);
123-
return (ObjectNode) versionNode.set(sync, newSyncNode);
124-
}
91+
@Nonnull
92+
private static ObjectNode createSyncNode() {
12593

126-
private static ObjectNode createBenchmarkNode(@Nonnull final ObjectNode syncNode,
127-
@Nonnull final String benchmark) {
128-
final ObjectNode newBenchmarkNode = JsonNodeFactory.instance.objectNode();
129-
newBenchmarkNode.set(EXECUTION_TIMES, JsonNodeFactory.instance.arrayNode());
130-
newBenchmarkNode.set(AVERAGE, JsonNodeFactory.instance.numberNode(0));
131-
newBenchmarkNode.set(DIFF, JsonNodeFactory.instance.numberNode(0));
94+
final ObjectNode newSyncNode = JsonNodeFactory.instance.objectNode();
95+
newSyncNode.set(CREATES_ONLY, createBenchmarkNode());
96+
newSyncNode.set(UPDATES_ONLY, createBenchmarkNode());
97+
newSyncNode.set(CREATES_AND_UPDATES, createBenchmarkNode());
13298

133-
syncNode.set(benchmark, newBenchmarkNode);
134-
return syncNode;
99+
return newSyncNode;
135100
}
136101

137-
static double calculateDiff(@Nonnull final String version,
138-
@Nonnull final String sync,
139-
@Nonnull final String benchmark,
140-
final double average) throws IOException {
141-
final JsonNode rootNode = new ObjectMapper().readTree(getFileContent(BENCHMARK_RESULTS_FILE_PATH));
142-
return calculateDiff(rootNode, version, sync, benchmark, average);
143-
}
102+
@Nonnull
103+
private static ObjectNode createBenchmarkNode() {
144104

145-
private static double calculateDiff(@Nonnull final JsonNode originalRoot,
146-
@Nonnull final String version,
147-
@Nonnull final String sync,
148-
@Nonnull final String benchmark,
149-
final double average) {
150-
return getLatestVersionName(originalRoot, version)
151-
.map(latestVersionName -> originalRoot.get(latestVersionName)
152-
.get(sync)
153-
.get(benchmark)
154-
.get(AVERAGE))
155-
.map(latestAverageNode -> average - latestAverageNode.asDouble())
156-
// if there is no latest version (meaning this is the first benchmark for this module)
157-
// then return a 0 diff.
158-
.orElse(0.0);
159-
}
105+
final ObjectNode newBenchmarkNode = JsonNodeFactory.instance.objectNode();
106+
newBenchmarkNode.set(EXECUTION_TIME, JsonNodeFactory.instance.numberNode(0));
160107

161-
private static Optional<String> getLatestVersionName(@Nonnull final JsonNode originalRoot,
162-
@Nonnull final String currentVersionName) {
163-
return toStream(originalRoot.fieldNames()).reduce((firstVersion, secondVersion) ->
164-
!currentVersionName.equals(secondVersion) ? secondVersion : firstVersion);
108+
return newBenchmarkNode;
165109
}
166110

167-
168-
private static String getFileContent(@Nonnull final String path) throws IOException {
169-
final byte[] fileBytes = Files.readAllBytes(Paths.get(path));
170-
return new String(fileBytes, UTF8_CHARSET);
111+
private static void writeToFile(@Nonnull final String content) throws IOException {
112+
Files.write(Paths.get(BENCHMARK_RESULTS_FILE_PATH), content.getBytes(UTF8_CHARSET));
171113
}
172114

173-
private static void writeToFile(@Nonnull final String content, @Nonnull final String path) throws IOException {
174-
Files.write(Paths.get(path), content.getBytes(UTF8_CHARSET));
115+
private BenchmarkUtils() {
175116
}
176117
}

src/benchmark/java/com/commercetools/sync/benchmark/CategorySyncBenchmark.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.commercetools.sync.benchmark;
22

3-
import com.commercetools.sync.commons.utils.SyncSolutionInfo;
43
import org.junit.Ignore;
54
import org.junit.Test;
65

@@ -17,19 +16,19 @@ public class CategorySyncBenchmark {
1716
@Test
1817
public void sync_NewCategories_ShouldCreateCategories() throws IOException {
1918
// TODO: SHOULD BE IMPLEMENTED.
20-
saveNewResult(SyncSolutionInfo.LIB_VERSION, CATEGORY_SYNC, CREATES_ONLY, 20000);
19+
saveNewResult(CATEGORY_SYNC, CREATES_ONLY, 20000);
2120
}
2221

2322
@Test
2423
public void sync_ExistingCategories_ShouldUpdateCategories() throws IOException {
2524
// TODO: SHOULD BE IMPLEMENTED.
26-
saveNewResult(SyncSolutionInfo.LIB_VERSION, CATEGORY_SYNC, UPDATES_ONLY, 10000);
25+
saveNewResult(CATEGORY_SYNC, UPDATES_ONLY, 10000);
2726
}
2827

2928
@Test
3029
public void sync_WithSomeExistingCategories_ShouldSyncCategories() throws IOException {
3130
// TODO: SHOULD BE IMPLEMENTED.
32-
saveNewResult(SyncSolutionInfo.LIB_VERSION, CATEGORY_SYNC, CREATES_AND_UPDATES, 30000);
31+
saveNewResult(CATEGORY_SYNC, CREATES_AND_UPDATES, 30000);
3332
}
3433

3534
}

src/benchmark/java/com/commercetools/sync/benchmark/InventorySyncBenchmark.java

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.commercetools.sync.benchmark;
22

3-
import com.commercetools.sync.commons.utils.SyncSolutionInfo;
43
import com.commercetools.sync.inventories.InventorySync;
54
import com.commercetools.sync.inventories.InventorySyncOptions;
65
import com.commercetools.sync.inventories.InventorySyncOptionsBuilder;
@@ -26,9 +25,8 @@
2625
import static com.commercetools.sync.benchmark.BenchmarkUtils.CREATES_ONLY;
2726
import static com.commercetools.sync.benchmark.BenchmarkUtils.INVENTORY_SYNC;
2827
import static com.commercetools.sync.benchmark.BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST;
29-
import static com.commercetools.sync.benchmark.BenchmarkUtils.THRESHOLD;
28+
import static com.commercetools.sync.benchmark.BenchmarkUtils.THRESHOLD_EXCEEDED_ERROR;
3029
import static com.commercetools.sync.benchmark.BenchmarkUtils.UPDATES_ONLY;
31-
import static com.commercetools.sync.benchmark.BenchmarkUtils.calculateDiff;
3230
import static com.commercetools.sync.benchmark.BenchmarkUtils.saveNewResult;
3331
import static com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics.assertThat;
3432
import static com.commercetools.sync.integration.commons.utils.ChannelITUtils.deleteChannels;
@@ -57,25 +55,23 @@ public static void tearDown() {
5755

5856
@Test
5957
public void sync_NewInventories_ShouldCreateInventories() throws IOException {
58+
// preparation
6059
final List<InventoryEntryDraft> inventoryEntryDrafts = buildInventoryDrafts(NUMBER_OF_RESOURCE_UNDER_TEST);
61-
62-
6360
final InventorySyncOptions inventorySyncOptions = InventorySyncOptionsBuilder.of(CTP_TARGET_CLIENT)
6461
.build();
6562
final InventorySync inventorySync = new InventorySync(inventorySyncOptions);
6663

67-
64+
// benchmark
6865
final long beforeSync = System.currentTimeMillis();
6966
final InventorySyncStatistics inventorySyncStatistics =
7067
executeBlocking(inventorySync.sync(inventoryEntryDrafts));
7168
final long totalTime = System.currentTimeMillis() - beforeSync;
7269

7370

74-
// Caclulate sync time and assert on threshold
75-
final double diff = calculateDiff(SyncSolutionInfo.LIB_VERSION, INVENTORY_SYNC, CREATES_ONLY, totalTime);
76-
assertThat(diff).withFailMessage(format("Diff of benchmark '%e' is longer than expected"
77-
+ " threshold of '%d'.", diff, THRESHOLD))
78-
.isLessThanOrEqualTo(THRESHOLD);
71+
// assert on threshold (based on history of benchmarks; highest was ~9 seconds)
72+
final int threshold = 18000; // double of the highest benchmark
73+
assertThat(totalTime).withFailMessage(format(THRESHOLD_EXCEEDED_ERROR, totalTime, threshold))
74+
.isLessThan(threshold);
7975

8076
// Assert actual state of CTP project (total number of existing inventories)
8177
final CompletableFuture<Integer> totalNumberOfInventories =
@@ -92,21 +88,21 @@ public void sync_NewInventories_ShouldCreateInventories() throws IOException {
9288
assertThat(inventorySyncStatistics)
9389
.hasValues(NUMBER_OF_RESOURCE_UNDER_TEST, NUMBER_OF_RESOURCE_UNDER_TEST, 0, 0);
9490

95-
saveNewResult(SyncSolutionInfo.LIB_VERSION, INVENTORY_SYNC, CREATES_ONLY, totalTime);
91+
saveNewResult(INVENTORY_SYNC, CREATES_ONLY, totalTime);
9692
}
9793

9894
@Ignore
9995
@Test
10096
public void sync_ExistingInventories_ShouldUpdateInventories() throws IOException {
10197
// TODO: SHOULD BE IMPLEMENTED.
102-
saveNewResult(SyncSolutionInfo.LIB_VERSION, INVENTORY_SYNC, UPDATES_ONLY, 50000);
98+
saveNewResult(INVENTORY_SYNC, UPDATES_ONLY, 50000);
10399
}
104100

105101
@Ignore
106102
@Test
107103
public void sync_WithSomeExistingInventories_ShouldSyncInventories() throws IOException {
108104
// TODO: SHOULD BE IMPLEMENTED.
109-
saveNewResult(SyncSolutionInfo.LIB_VERSION, INVENTORY_SYNC, CREATES_AND_UPDATES, 30000);
105+
saveNewResult(INVENTORY_SYNC, CREATES_AND_UPDATES, 30000);
110106
}
111107

112108
@Nonnull

0 commit comments

Comments
 (0)