Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .project
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,15 @@
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
<filteredResources>
<filter>
<id>1783424300172</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
Expand Down Expand Up @@ -116,48 +116,59 @@ public String AsJson(int page, int pageSize) {
}

public List<T> getPagedResult(int page, int pageSize) {
List<T> builder = new ArrayList<>();
final int start;
final int end;
if (page == -1 || pageSize == -1) {
start = 0;
end = collection.size();
} else {
start = (page - 1) * pageSize;
end = Math.min(page * pageSize, collection.size());
if (collection == null || collection.isEmpty()) {
return List.of();
}
int i = 0;
for (T e : collection.stream().sorted(getComparator()).collect(Collectors.toList())) {
if (i >= start && i < end) {
builder.add(e);

List<T> collectionList = new ArrayList<>(collection);

// Pre-compute fields once per element
Map<Object, Object> fieldCache = new IdentityHashMap<>(collectionList.size());
for (T item : collectionList) {
if (item != null) {
fieldCache.put(item, getField(item, sortField));
}
i++;
}
return Collections.unmodifiableList(builder);
}

public Predicate<T> getPredicate() {
return predicate;
}
boolean sortOrderDescending = sortOrder.equalsIgnoreCase(DESCENDING);

public Comparator<T> getComparator() {
return (left, right) -> {
Comparator<T> cachedComparator = (left, right) -> {
try {
Object leftValue = getField(left, sortField);
Object rightValue = getField(right, sortField);
if (leftValue instanceof Comparable l && rightValue instanceof Comparable r) {
if (sortOrder.equalsIgnoreCase(DESCENDING)) {
Object leftValue = fieldCache.get(left);
Object rightValue = fieldCache.get(right);
if (sortOrderDescending) {
if (rightValue instanceof Comparable r) {
return r.compareTo(leftValue);
} else {
}
} else {
if (leftValue instanceof Comparable l) {
return l.compareTo(rightValue);
}
}
return 0;
} catch (Exception e) {
//LOG.info("Exception sorting destinations", e);
return 0;
}
};

collectionList.sort(cachedComparator);

if (page == -1 || pageSize == -1) {
return collectionList;
}

int start = (page - 1) * pageSize;
int size = collectionList.size();
if (start >= size || start < 0) {
return List.of();
}
int end = Math.min(page * pageSize, size);

return collectionList.subList(start, end);
}

public Predicate<T> getPredicate() {
return predicate;
}

abstract Object getField(T t, String fieldName);
Expand All @@ -171,12 +182,11 @@ public void setOptions(String options) {
}
if (predicate != null) {
predicate.addFilterParts(createFilterPredicates(json));
if ((json.containsKey(SORT_COLUMN) || json.containsKey(SORT_FIELD)) && json.containsKey(SORT_ORDER)) {
if (json.containsKey(SORT_COLUMN)) {
this.sortField = json.getString(SORT_COLUMN);
} else {
this.sortField = json.getString(SORT_FIELD);
}
if ((json.containsKey(SORT_FIELD)) && json.containsKey(SORT_ORDER)) {
this.sortField = json.getString(SORT_FIELD);
this.sortOrder = json.getString(SORT_ORDER);
} else if (json.containsKey(SORT_COLUMN)) {
this.sortField = json.getString(SORT_COLUMN);
this.sortOrder = json.getString(SORT_ORDER);
}
}
Expand All @@ -189,7 +199,8 @@ private List<V> createFilterPredicates(JsonObject json) {
predicates.add(predicate.createFilterPart(json.getString(FILTER_FIELD), json.getString(FILTER_OPERATION), json.getString(FILTER_VALUE)));
} else {
for (JsonValue jsonValue : jsonArray) {
predicates.add(predicate.createFilterPart(((JsonObject)jsonValue).getString(FILTER_FIELD), ((JsonObject)jsonValue).getString(FILTER_OPERATION), ((JsonObject)jsonValue).getString(FILTER_VALUE)));
JsonObject jsonObject = (JsonObject) jsonValue;
predicates.add(predicate.createFilterPart(jsonObject.getString(FILTER_FIELD), jsonObject.getString(FILTER_OPERATION), jsonObject.getString(FILTER_VALUE)));
}
}
return predicates;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ public String getDefaultOrderColumn() {
assertEquals(list.size(), myView.getPagedResult(123, -1).size());
assertEquals(list.size(), myView.getPagedResult(-1, 123).size());

// page 0 - not really valid but still "works"
assertEquals(0, myView.getPagedResult(0, 123).size());


assertEquals(123, myView.getPagedResult(1, 123).size());

// last page
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.tests.performance.jmh;

import org.apache.activemq.artemis.api.core.management.AddressControl;
import org.apache.activemq.artemis.core.management.impl.view.AddressField;
import org.apache.activemq.artemis.core.management.impl.view.AddressView;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.Mode;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@State(Scope.Benchmark)
@Fork(value = 1)
@Warmup(iterations = 2, time = 1)
@Measurement(iterations = 5, time = 1)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public class AbstractViewPerfTest {

@Param({"1000", "10000"})
private int addressCount;

@Param({"50"})
private int pageSize;

private AddressView view;
private int middlePage;

@Setup(Level.Trial)
public void init() {
view = new AddressView(null);

// 1. Configure the view options using the internal JSON parsing mechanism
// This switches the target field to MESSAGE_COUNT and sets the sorting loop active
String optionsJson = "{"
+ "\"field\":\"\","
+ "\"operation\":\"\","
+ "\"value\":\"\","
+ "\"sortField\":\"" + AddressField.MESSAGE_COUNT.getName() + "\","
+ "\"sortOrder\":\"desc\""
+ "}";
view.setOptions(optionsJson);

// 2. Generate data rows
List<AddressControl> addresses = new ArrayList<>(addressCount);
for (int i = 0; i < addressCount; i++) {
addresses.add(stubAddress(i));
}

// 3. Shuffle dataset to break TimSort's best-case O(N) shortcut paths
Collections.shuffle(addresses);

view.setCollection(addresses);
middlePage = (addressCount / 2) / pageSize + 1;
}

@Benchmark
public List<AddressControl> testFirstPage() {
return view.getPagedResult(0, pageSize);
}

@Benchmark
public List<AddressControl> testMiddlePage() {
return view.getPagedResult(middlePage, pageSize);
}

@Benchmark
public List<AddressControl> testAllResults() {
return view.getPagedResult(0, -1);
}

private static AddressControl stubAddress(long id) {
final long simulatedMessageCount = (id * 31) % 10000;
final String addressName = "address-" + id;

return new AddressControl() {
@Override public long getId() {
return id;
}
@Override public String getAddress() {
return addressName;
}

@Override
public long getMessageCount() {
// Mimic production cost: CPU loop simulating reading multiple live
// internal queue components and consolidating dynamic tracking fields.
long infrastructureTraversalCost = 0;
for (int i = 0; i < 4; i++) {
infrastructureTraversalCost += Thread.currentThread().hashCode() + simulatedMessageCount;
}
return simulatedMessageCount + (infrastructureTraversalCost & 0);
}

@Override public String[] getRoutingTypes() {
return new String[0];
}
@Override public String getRoutingTypesAsJSON() {
return "[]";
}
@Override public Object[] getRoles() {
return new Object[0];
}
@Override public String getRolesAsJSON() {
return "[]";
}
@Override public long getAddressSize() {
return 0L;
}
@Override public int getMaxPageReadBytes() {
return 0;
}
@Override public int getMaxPageReadMessages() {
return 0;
}
@Override public int getPrefetchPageBytes() {
return 0;
}
@Override public int getPrefetchPageMessages() {
return 0;
}
@Override public void schedulePageCleanup() {
}
@Override public long getNumberOfMessages() {
return 0L;
}
@Override public String[] getRemoteQueueNames() {
return new String[0];
}
@Override public String[] getQueueNames() {
return new String[0];
}
@Override public String[] getAllQueueNames() {
return new String[0];
}
@Override public long getNumberOfPages() {
return 0L;
}
@Override public boolean isPaging() {
return false;
}
@Override public int getAddressLimitPercent() {
return 0;
}
@Override public boolean block() {
return false;
}
@Override public void unblock() {
}
@Override public boolean isBlockedViaManagement() {
return false;
}
@Override public long getNumberOfBytesPerPage() {
return 0L;
}
@Override public String[] getBindingNames() {
return new String[0];
}
@Override public long getQueueCount() {
return 0L;
}
@Override public long getRoutedMessageCount() {
return 0L;
}
@Override public long getUnRoutedMessageCount() {
return 0L;
}
@Override public void pause() {
}
@Override public void pause(boolean persist) {
}
@Override public void resume() {
}
@Override public boolean isPaused() {
return false;
}
@Override public boolean isRetroactiveResource() {
return false;
}
@Override public long getCurrentDuplicateIdCacheSize() {
return 0L;
}
@Override public boolean clearDuplicateIdCache() {
return false;
}
@Override public boolean isAutoCreated() {
return false;
}
@Override public boolean isInternal() {
return false;
}
@Override public boolean isTemporary() {
return false;
}
@Override public long purge() {
return 0L;
}
@Override public void replay(String target, String filter) {
}
@Override public void replay(String startScan, String endScan, String target, String filter) {
}
@Override public String sendMessage(Map<String, String> headers, int type, String body, boolean durable, String user, String password) {
return null;
}
@Override public String sendMessage(Map<String, String> headers, int type, String body, boolean durable, String user, String password, boolean createMessageId) {
return null;
}
};
}
}
Loading