Skip to content

Commit 2a34a9a

Browse files
authored
Add benchmarks for category sync (#1132)
* Add benchmarks for category sync * Make cleanup of test resources faster * Run spotless
1 parent 635f73a commit 2a34a9a

File tree

9 files changed

+333
-13
lines changed

9 files changed

+333
-13
lines changed
Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
package com.commercetools.sync.benchmark;
2+
3+
import static com.commercetools.sync.benchmark.BenchmarkUtils.CATEGORY_SYNC;
4+
import static com.commercetools.sync.benchmark.BenchmarkUtils.CREATES_AND_UPDATES;
5+
import static com.commercetools.sync.benchmark.BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST;
6+
import static com.commercetools.sync.benchmark.BenchmarkUtils.UPDATES_ONLY;
7+
import static com.commercetools.sync.benchmark.BenchmarkUtils.saveNewResult;
8+
import static com.commercetools.sync.integration.commons.utils.CategoryITUtils.getCategoryDrafts;
9+
import static com.commercetools.sync.integration.commons.utils.TestClientUtils.CTP_TARGET_CLIENT;
10+
import static org.assertj.core.api.Assertions.assertThat;
11+
12+
import com.commercetools.api.client.QueryUtils;
13+
import com.commercetools.api.models.category.Category;
14+
import com.commercetools.api.models.category.CategoryDraft;
15+
import com.commercetools.api.models.category.CategoryDraftBuilder;
16+
import com.commercetools.api.models.category.CategoryPagedQueryResponse;
17+
import com.commercetools.api.models.category.CategoryUpdateAction;
18+
import com.commercetools.api.models.common.LocalizedString;
19+
import com.commercetools.sync.categories.CategorySync;
20+
import com.commercetools.sync.categories.CategorySyncOptions;
21+
import com.commercetools.sync.categories.CategorySyncOptionsBuilder;
22+
import com.commercetools.sync.categories.helpers.CategorySyncStatistics;
23+
import com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics;
24+
import com.commercetools.sync.commons.exceptions.SyncException;
25+
import com.commercetools.sync.commons.utils.QuadConsumer;
26+
import com.commercetools.sync.commons.utils.TriConsumer;
27+
import io.vrap.rmf.base.client.ApiHttpResponse;
28+
import java.io.IOException;
29+
import java.util.ArrayList;
30+
import java.util.List;
31+
import java.util.Optional;
32+
import java.util.concurrent.CompletableFuture;
33+
import java.util.concurrent.CompletionStage;
34+
import org.junit.jupiter.api.BeforeAll;
35+
import org.junit.jupiter.api.BeforeEach;
36+
import org.junit.jupiter.api.Test;
37+
38+
class CategorySyncBenchmark {
39+
40+
private static final int CATEGORY_BENCHMARKS_CREATE_ACTION_THRESHOLD = 23_000;
41+
private static final int CATEGORY_BENCHMARKS_UPDATE_ACTION_THRESHOLD = 23_000;
42+
private static final int CATEGORY_BENCHMARKS_CREATE_AND_UPDATE_ACTION_THRESHOLD = 23_000;
43+
private List<String> errorCallBackMessages;
44+
private List<String> warningCallBackMessages;
45+
private List<Throwable> errorCallBackExceptions;
46+
private CategorySyncOptions categorySyncOptions;
47+
48+
@BeforeAll
49+
static void setup() {
50+
deleteAllCategories();
51+
}
52+
53+
@BeforeEach
54+
void setupTest() {
55+
deleteAllCategories();
56+
clearSyncTestCollections();
57+
categorySyncOptions = buildSyncOptions();
58+
}
59+
60+
private void clearSyncTestCollections() {
61+
errorCallBackMessages = new ArrayList<>();
62+
warningCallBackMessages = new ArrayList<>();
63+
errorCallBackExceptions = new ArrayList<>();
64+
}
65+
66+
private CategorySyncOptions buildSyncOptions() {
67+
final QuadConsumer<
68+
SyncException, Optional<CategoryDraft>, Optional<Category>, List<CategoryUpdateAction>>
69+
errorCallback =
70+
(exception, newResource, oldResource, updateActions) -> {
71+
errorCallBackMessages.add(exception.getMessage());
72+
errorCallBackExceptions.add(exception.getCause());
73+
};
74+
75+
final TriConsumer<SyncException, Optional<CategoryDraft>, Optional<Category>> warningCallBack =
76+
(exception, newResource, oldResource) ->
77+
warningCallBackMessages.add(exception.getMessage());
78+
79+
return CategorySyncOptionsBuilder.of(CTP_TARGET_CLIENT)
80+
.errorCallback(errorCallback)
81+
.warningCallback(warningCallBack)
82+
.build();
83+
}
84+
85+
@Test
86+
void sync_NewCategories_ShouldCreateCategories() throws IOException {
87+
// preparation
88+
final List<CategoryDraft> categoryDrafts =
89+
getCategoryDraftsWithMetaDescription(BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST);
90+
91+
final CategorySync categorySync = new CategorySync(categorySyncOptions);
92+
93+
// benchmark
94+
final long beforeSyncTime = System.currentTimeMillis();
95+
final CategorySyncStatistics syncStatistics =
96+
categorySync.sync(categoryDrafts).toCompletableFuture().join();
97+
final long totalTime = System.currentTimeMillis() - beforeSyncTime;
98+
99+
// asserts
100+
assertThat(totalTime)
101+
.withFailMessage(
102+
String.format(
103+
BenchmarkUtils.THRESHOLD_EXCEEDED_ERROR,
104+
totalTime,
105+
CATEGORY_BENCHMARKS_CREATE_ACTION_THRESHOLD))
106+
.isLessThan(CATEGORY_BENCHMARKS_CREATE_ACTION_THRESHOLD);
107+
108+
AssertionsForStatistics.assertThat(syncStatistics)
109+
.hasValues(
110+
BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST,
111+
BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST,
112+
0,
113+
0);
114+
assertThat(errorCallBackExceptions).isEmpty();
115+
assertThat(errorCallBackMessages).isEmpty();
116+
assertThat(warningCallBackMessages).isEmpty();
117+
// Assert actual state of CTP project (total number of existing categories)
118+
final Long totalNumberOfCreatedCategories =
119+
CTP_TARGET_CLIENT
120+
.categories()
121+
.get()
122+
.execute()
123+
.thenApply(ApiHttpResponse::getBody)
124+
.thenApply(CategoryPagedQueryResponse::getTotal)
125+
.join();
126+
assertThat(totalNumberOfCreatedCategories)
127+
.isEqualTo(BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST);
128+
129+
if (BenchmarkUtils.SUBMIT_BENCHMARK_RESULT) {
130+
BenchmarkUtils.saveNewResult(CATEGORY_SYNC, BenchmarkUtils.CREATES_ONLY, totalTime);
131+
}
132+
}
133+
134+
@Test
135+
void sync_ExistingCategories_ShouldUpdateCategories() throws IOException {
136+
final List<CategoryDraft> categoryDrafts =
137+
getCategoryDraftsWithMetaDescription(BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST);
138+
139+
// Create drafts to target project with different meta descriptions
140+
CompletableFuture.allOf(
141+
categoryDrafts.stream()
142+
.map(CategoryDraftBuilder::of)
143+
.map(
144+
builder ->
145+
builder.metaDescription(LocalizedString.ofEnglish("oldMetaDescription")))
146+
.map(CategoryDraftBuilder::build)
147+
.map(draft -> CTP_TARGET_CLIENT.categories().create(draft).execute())
148+
.map(CompletableFuture::toCompletableFuture)
149+
.toArray(CompletableFuture[]::new))
150+
.join();
151+
152+
final CategorySync categorySync = new CategorySync(categorySyncOptions);
153+
154+
// benchmark
155+
final long beforeSyncTime = System.currentTimeMillis();
156+
final CategorySyncStatistics syncStatistics =
157+
categorySync.sync(categoryDrafts).toCompletableFuture().join();
158+
final long totalTime = System.currentTimeMillis() - beforeSyncTime;
159+
160+
// asserts
161+
assertThat(totalTime)
162+
.withFailMessage(
163+
String.format(
164+
BenchmarkUtils.THRESHOLD_EXCEEDED_ERROR,
165+
totalTime,
166+
CATEGORY_BENCHMARKS_UPDATE_ACTION_THRESHOLD))
167+
.isLessThan(CATEGORY_BENCHMARKS_UPDATE_ACTION_THRESHOLD);
168+
AssertionsForStatistics.assertThat(syncStatistics)
169+
.hasValues(
170+
BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST,
171+
0,
172+
BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST,
173+
0);
174+
assertThat(errorCallBackExceptions).isEmpty();
175+
assertThat(errorCallBackMessages).isEmpty();
176+
assertThat(warningCallBackMessages).isEmpty();
177+
178+
// Assert actual state of CTP project (number of updated categories)
179+
final Long numberOfUpdatedCategories =
180+
CTP_TARGET_CLIENT
181+
.categories()
182+
.get()
183+
.withWhere("metaDescription(en=:desc)")
184+
.withPredicateVar("desc", "newMetaDescription")
185+
.execute()
186+
.thenApply(ApiHttpResponse::getBody)
187+
.thenApply(CategoryPagedQueryResponse::getTotal)
188+
.join();
189+
assertThat(numberOfUpdatedCategories).isEqualTo(BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST);
190+
191+
// Assert actual state of CTP project (total number of existing categories)
192+
final Long totalNumberOfCategories =
193+
CTP_TARGET_CLIENT
194+
.categories()
195+
.get()
196+
.execute()
197+
.thenApply(ApiHttpResponse::getBody)
198+
.thenApply(CategoryPagedQueryResponse::getTotal)
199+
.join();
200+
assertThat(totalNumberOfCategories).isEqualTo(BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST);
201+
202+
if (BenchmarkUtils.SUBMIT_BENCHMARK_RESULT) {
203+
saveNewResult(CATEGORY_SYNC, UPDATES_ONLY, totalTime);
204+
}
205+
}
206+
207+
@Test
208+
void sync_WithSomeExistingCategories_ShouldSyncCategories() throws IOException {
209+
// preparation
210+
final List<CategoryDraft> categoryDrafts =
211+
getCategoryDraftsWithMetaDescription(NUMBER_OF_RESOURCE_UNDER_TEST);
212+
final int halfNumberOfDrafts = categoryDrafts.size() / 2;
213+
final List<CategoryDraft> firstHalfOfDrafts = categoryDrafts.subList(0, halfNumberOfDrafts);
214+
215+
// Create drafts to target project with different meta descriptions
216+
CompletableFuture.allOf(
217+
firstHalfOfDrafts.stream()
218+
.map(CategoryDraftBuilder::of)
219+
.map(
220+
builder ->
221+
builder.metaDescription(LocalizedString.ofEnglish("oldMetaDescription")))
222+
.map(CategoryDraftBuilder::build)
223+
.map(draft -> CTP_TARGET_CLIENT.categories().create(draft).execute())
224+
.map(CompletableFuture::toCompletableFuture)
225+
.toArray(CompletableFuture[]::new))
226+
.join();
227+
228+
final CategorySync categorySync = new CategorySync(categorySyncOptions);
229+
230+
// benchmark
231+
final long beforeSyncTime = System.currentTimeMillis();
232+
final CategorySyncStatistics syncStatistics =
233+
categorySync.sync(categoryDrafts).toCompletableFuture().join();
234+
final long totalTime = System.currentTimeMillis() - beforeSyncTime;
235+
236+
// asserts
237+
assertThat(totalTime)
238+
.withFailMessage(
239+
String.format(
240+
BenchmarkUtils.THRESHOLD_EXCEEDED_ERROR,
241+
totalTime,
242+
CATEGORY_BENCHMARKS_CREATE_AND_UPDATE_ACTION_THRESHOLD))
243+
.isLessThan(CATEGORY_BENCHMARKS_CREATE_AND_UPDATE_ACTION_THRESHOLD);
244+
245+
AssertionsForStatistics.assertThat(syncStatistics)
246+
.hasValues(
247+
BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST,
248+
halfNumberOfDrafts,
249+
halfNumberOfDrafts,
250+
0);
251+
assertThat(errorCallBackExceptions).isEmpty();
252+
assertThat(errorCallBackMessages).isEmpty();
253+
assertThat(warningCallBackMessages).isEmpty();
254+
255+
// Assert actual state of CTP project (number of updated categories)
256+
final Long numberOfUpdatedCategories =
257+
CTP_TARGET_CLIENT
258+
.categories()
259+
.get()
260+
.withWhere("metaDescription(en=:desc)")
261+
.withPredicateVar("desc", "newMetaDescription")
262+
.execute()
263+
.thenApply(ApiHttpResponse::getBody)
264+
.thenApply(CategoryPagedQueryResponse::getTotal)
265+
.join();
266+
assertThat(numberOfUpdatedCategories).isEqualTo(BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST);
267+
268+
// Assert actual state of CTP project (total number of existing categories)
269+
final Long totalNumberOfCategories =
270+
CTP_TARGET_CLIENT
271+
.categories()
272+
.get()
273+
.execute()
274+
.thenApply(ApiHttpResponse::getBody)
275+
.thenApply(CategoryPagedQueryResponse::getTotal)
276+
.join();
277+
assertThat(totalNumberOfCategories).isEqualTo(BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST);
278+
279+
if (BenchmarkUtils.SUBMIT_BENCHMARK_RESULT) {
280+
saveNewResult(CATEGORY_SYNC, CREATES_AND_UPDATES, totalTime);
281+
}
282+
}
283+
284+
private List<CategoryDraft> getCategoryDraftsWithMetaDescription(final int numberOfCategories) {
285+
final List<CategoryDraft> categoryDraftsWithDescription = new ArrayList<>();
286+
final List<CategoryDraft> categoryDrafts = getCategoryDrafts(null, numberOfCategories, false);
287+
for (CategoryDraft categoryDraft : categoryDrafts) {
288+
final CategoryDraftBuilder categoryDraftBuilder =
289+
CategoryDraftBuilder.of(categoryDraft)
290+
.metaDescription(LocalizedString.ofEnglish("newMetaDescription"));
291+
categoryDraftsWithDescription.add(categoryDraftBuilder.build());
292+
}
293+
return categoryDraftsWithDescription;
294+
}
295+
296+
private static void deleteAllCategories() {
297+
QueryUtils.queryAll(
298+
CTP_TARGET_CLIENT.categories().get(),
299+
fetchedCategories -> {
300+
CompletableFuture.allOf(
301+
fetchedCategories.stream()
302+
.map(category -> deleteCategory(category))
303+
.map(CompletionStage::toCompletableFuture)
304+
.toArray(CompletableFuture[]::new))
305+
.join();
306+
})
307+
.toCompletableFuture()
308+
.join();
309+
}
310+
311+
private static CompletionStage<Category> deleteCategory(final Category category) {
312+
return CTP_TARGET_CLIENT
313+
.categories()
314+
.delete(category)
315+
.execute()
316+
.thenApply(ApiHttpResponse::getBody);
317+
}
318+
}

src/integration-test/java/com/commercetools/sync/integration/commons/utils/CategoryITUtils.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,13 @@ public final class CategoryITUtils {
5151
* can be used for integration tests to mimic existing categories in a target CTP project for
5252
* example. All the newly created category drafts will have {@code parentCategory} as a parent.
5353
*
54-
* @param numberOfCategories the number of category drafts to create.
5554
* @param parentCategory the parent of the drafts.
55+
* @param numberOfCategories the number of category drafts to create.
56+
* @param withCustom
5657
* @return a list of CategoryDrafts.
5758
*/
5859
public static List<CategoryDraft> getCategoryDrafts(
59-
@Nullable final Category parentCategory, final int numberOfCategories) {
60+
@Nullable final Category parentCategory, final int numberOfCategories, boolean withCustom) {
6061
List<CategoryDraft> categoryDrafts = new ArrayList<>();
6162
for (int i = 0; i < numberOfCategories; i++) {
6263
final LocalizedString name = LocalizedString.of(Locale.ENGLISH, format("draft%s", i + 1));
@@ -76,7 +77,7 @@ public static List<CategoryDraft> getCategoryDrafts(
7677
.description(description)
7778
.key(key)
7879
.orderHint(orderHint)
79-
.custom(getCustomFieldsDraft())
80+
.custom(withCustom ? getCustomFieldsDraft() : null)
8081
.build();
8182
categoryDrafts.add(categoryDraft);
8283
}
@@ -100,7 +101,7 @@ public static List<CategoryDraft> getCategoryDraftsWithPrefix(
100101
final int numberOfCategories) {
101102
final List<CategoryDraft> categoryDraftsWithPrefix = new ArrayList<>();
102103
final List<CategoryDraft> categoryDrafts =
103-
getCategoryDrafts(parentCategory, numberOfCategories);
104+
getCategoryDrafts(parentCategory, numberOfCategories, true);
104105
for (CategoryDraft categoryDraft : categoryDrafts) {
105106
final LocalizedString newCategoryName =
106107
LocalizedString.of(locale, format("%s%s", prefix, categoryDraft.getName().get(locale)));

src/integration-test/java/com/commercetools/sync/integration/ctpprojectsource/categories/CategorySyncIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ void setupTest() {
6969
CategoryITUtils.deleteAllCategories(TestClientUtils.CTP_SOURCE_CLIENT);
7070

7171
CategoryITUtils.ensureCategories(
72-
TestClientUtils.CTP_TARGET_CLIENT, CategoryITUtils.getCategoryDrafts(null, 2));
72+
TestClientUtils.CTP_TARGET_CLIENT, CategoryITUtils.getCategoryDrafts(null, 2, true));
7373

7474
callBackErrorResponses = new ArrayList<>();
7575
callBackExceptions = new ArrayList<>();

src/integration-test/java/com/commercetools/sync/integration/ctpprojectsource/products/ProductReferenceResolverIT.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,12 @@ static void setup() {
8282
TestClientUtils.CTP_SOURCE_CLIENT);
8383

8484
CategoryITUtils.ensureCategories(
85-
TestClientUtils.CTP_TARGET_CLIENT, CategoryITUtils.getCategoryDrafts(null, 2));
85+
TestClientUtils.CTP_TARGET_CLIENT, CategoryITUtils.getCategoryDrafts(null, 2, true));
8686
categoryReferencesWithIds =
8787
CategoryITUtils.getReferencesWithIds(
8888
CategoryITUtils.ensureCategories(
89-
TestClientUtils.CTP_SOURCE_CLIENT, CategoryITUtils.getCategoryDrafts(null, 2)));
89+
TestClientUtils.CTP_SOURCE_CLIENT,
90+
CategoryITUtils.getCategoryDrafts(null, 2, true)));
9091

9192
ProductTypeITUtils.ensureProductType(
9293
PRODUCT_TYPE_RESOURCE_PATH, TestClientUtils.CTP_TARGET_CLIENT);

src/integration-test/java/com/commercetools/sync/integration/ctpprojectsource/products/ProductSyncIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,13 @@ static void setup() {
134134

135135
final List<Category> targetCategories =
136136
CategoryITUtils.ensureCategories(
137-
TestClientUtils.CTP_TARGET_CLIENT, CategoryITUtils.getCategoryDrafts(null, 2));
137+
TestClientUtils.CTP_TARGET_CLIENT, CategoryITUtils.getCategoryDrafts(null, 2, true));
138138
targetCategoryReferencesWithIds = CategoryITUtils.getReferencesWithIds(targetCategories);
139139
targetCategoryResourceIdentifiers =
140140
CategoryITUtils.getResourceIdentifiersWithIds(targetCategories);
141141
final List<Category> sourceCategories =
142142
CategoryITUtils.ensureCategories(
143-
TestClientUtils.CTP_SOURCE_CLIENT, CategoryITUtils.getCategoryDrafts(null, 2));
143+
TestClientUtils.CTP_SOURCE_CLIENT, CategoryITUtils.getCategoryDrafts(null, 2, true));
144144
sourceCategoryReferencesWithIds = CategoryITUtils.getReferencesWithIds(sourceCategories);
145145
sourceCategoryResourceIdentifiers =
146146
CategoryITUtils.getResourceIdentifiersWithIds(sourceCategories);

0 commit comments

Comments
 (0)