ARTEMIS-4814 Make direct binding lookup time constant

Currently, with 500K+ queues, the cleanup step of TempQueueCleanerUpper
requires invoking WildcardAddressManager#getDirectBindings, which is
O(k) in the number of queues.

From method profiling, this can consume up to 95% of our CPU time when
needing to clean up many of these.

Add a new map to keep track of the direct bindings, and add a test
assertion that fails if we don't properly remove it.
This commit is contained in:
Josh Byster 2024-06-11 22:10:54 -05:00 committed by clebertsuconic
parent ee9d016f6c
commit 166adf4bfb
2 changed files with 23 additions and 10 deletions

View File

@ -68,6 +68,8 @@ public class SimpleAddressManager implements AddressManager {
private final ConcurrentMap<SimpleString, Pair<Binding, Address>> nameMap = new ConcurrentHashMap<>();
private final ConcurrentMap<SimpleString, Collection<Binding>> directBindingMap = new ConcurrentHashMap<>();
private final BindingsFactory bindingsFactory;
protected final MetricsManager metricsManager;
@ -100,6 +102,11 @@ public class SimpleAddressManager implements AddressManager {
if (nameMap.putIfAbsent(binding.getUniqueName(), bindingAddressPair) != null) {
throw ActiveMQMessageBundle.BUNDLE.bindingAlreadyExists(binding);
}
directBindingMap.compute(binding.getAddress(), (key, value) -> {
Collection<Binding> bindingList = value == null ? new ArrayList<>() : value;
bindingList.add(binding);
return bindingList;
});
if (logger.isTraceEnabled()) {
logger.trace("Adding binding {} with address = {}", binding, binding.getUniqueName(), new Exception("trace"));
@ -116,7 +123,18 @@ public class SimpleAddressManager implements AddressManager {
return null;
}
removeBindingInternal(binding.getA().getAddress(), uniqueName);
SimpleString address = binding.getA().getAddress();
removeBindingInternal(address, uniqueName);
directBindingMap.compute(address, (key, value) -> {
if (value == null) {
return null;
}
value.remove(binding.getA());
if (value.isEmpty()) {
return null;
}
return value;
});
return binding.getA();
}
@ -160,15 +178,7 @@ public class SimpleAddressManager implements AddressManager {
@Override
public Collection<Binding> getDirectBindings(final SimpleString address) throws Exception {
SimpleString realAddress = CompositeAddress.extractAddressName(address);
Collection<Binding> bindings = new ArrayList<>();
nameMap.forEach((bindingUniqueName, bindingAddressPair) -> {
if (bindingAddressPair.getA().getAddress().equals(realAddress)) {
bindings.add(bindingAddressPair.getA());
}
});
return bindings;
return new ArrayList<>(directBindingMap.getOrDefault(realAddress, Collections.emptyList()));
}
@Override

View File

@ -202,6 +202,7 @@ public class WildcardAddressManagerUnitTest extends ActiveMQTestBase {
assertEquals(0, ad.getAddresses().size());
assertEquals(0, ad.getBindings().count());
assertEquals(0, ad.getDirectBindings(SimpleString.of("Topic1.>")).size());
}
@Test
@ -374,6 +375,8 @@ public class WildcardAddressManagerUnitTest extends ActiveMQTestBase {
// publish again, read only
ad.getBindingsForRoutingAddress(pubAddr);
ad.getDirectBindings(pubAddr);
} catch (Exception e) {
e.printStackTrace();
oops.set(e);