Skip to content

Commit

Permalink
ARTEMIS-5004 Clean up federation address consumer bindings proactively
Browse files Browse the repository at this point in the history
When an address consumer explicitly closes or is closed we should remove the
address binding for that consumer right away instead of waiting for possible
configured auto delete as the demand is gone and we don't need to binding to
stick around any longer.
  • Loading branch information
tabish121 authored and gemmellr committed Aug 21, 2024
1 parent 84f4d73 commit b58191b
Show file tree
Hide file tree
Showing 3 changed files with 360 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import static org.apache.activemq.artemis.protocol.amqp.proton.AmqpSupport.TOPIC_CAPABILITY;
import static org.apache.activemq.artemis.protocol.amqp.proton.AmqpSupport.verifyOfferedCapabilities;

import java.lang.invoke.MethodHandles;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -54,9 +55,12 @@
import org.apache.qpid.proton.amqp.Symbol;
import org.apache.qpid.proton.amqp.messaging.Source;
import org.apache.qpid.proton.amqp.transport.AmqpError;
import org.apache.qpid.proton.amqp.transport.ErrorCondition;
import org.apache.qpid.proton.amqp.transport.ReceiverSettleMode;
import org.apache.qpid.proton.engine.Connection;
import org.apache.qpid.proton.engine.Sender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* {@link SenderController} used when an AMQP federation Address receiver is created
Expand All @@ -67,6 +71,10 @@
*/
public final class AMQPFederationAddressSenderController extends AMQPFederationBaseSenderController {

private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

private ProtonServerSenderContext senderContext;

public AMQPFederationAddressSenderController(AMQPSessionContext session) throws ActiveMQAMQPException {
super(session);
}
Expand All @@ -88,6 +96,9 @@ public Consumer init(ProtonServerSenderContext senderContext) throws Exception {
throw new ActiveMQAMQPNotImplementedException("Null source lookup not supported on federation links.");
}

// Store for use during link close
this.senderContext = senderContext;

// Match the settlement mode of the remote instead of relying on the default of MIXED.
sender.setSenderSettleMode(sender.getRemoteSenderSettleMode());
// We don't currently support SECOND so enforce that the answer is always FIRST
Expand Down Expand Up @@ -197,6 +208,38 @@ public Consumer init(ProtonServerSenderContext senderContext) throws Exception {
return (Consumer) sessionSPI.createSender(senderContext, queueName, null, false);
}

@Override
protected void handleLinkRemotelyClosed() {
// Remote closed indicating there was no demand, so we can cleanup the federation binding
deleteAddressFederationBindingIfPresent();
}

@Override
protected void handleLinkLocallyClosed(ErrorCondition error) {
// Local side forcibly removed the federation consumer so we should ensure the binding is removed.
deleteAddressFederationBindingIfPresent();
}

private void deleteAddressFederationBindingIfPresent() {
if (senderContext == null) {
return;
}

try {
final Sender sender = senderContext.getSender();
final Source source = (Source) sender.getRemoteSource();
final SimpleString queueName = SimpleString.of(sender.getName());
final RoutingType routingType = getRoutingType(source);

final QueueQueryResult queueQuery = sessionSPI.queueQuery(queueName, routingType, false);
if (queueQuery.isExists()) {
sessionSPI.deleteQueue(queueName);
}
} catch (Exception e) {
logger.debug("Federation address sender link closed cleanup caught error: ", e);
}
}

@SuppressWarnings("unchecked")
private String getJMSSelectorFromFilters(Source source) throws ActiveMQAMQPException {
final Map.Entry<Symbol, DescribedType> jmsSelector = AmqpSupport.findFilter(source.getFilter(), AmqpSupport.JMS_SELECTOR_FILTER_IDS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,20 @@ public AMQPSessionCallback getSessionCallback() {
}

@Override
public void close() throws Exception {
public final void close() throws Exception {
if (federation != null) {
federation.removeLinkClosedInterceptor(controllerId);
}

handleLinkRemotelyClosed();
}

protected void handleLinkRemotelyClosed() {
// Default does nothing.
}

@Override
public void close(ErrorCondition error) {
public final void close(ErrorCondition error) {
if (error != null && AmqpError.RESOURCE_DELETED.equals(error.getCondition())) {
if (resourceDeletedAction != null) {
resourceDeletedAction.accept(error);
Expand All @@ -96,6 +102,12 @@ public void close(ErrorCondition error) {
if (federation != null) {
federation.removeLinkClosedInterceptor(controllerId);
}

handleLinkLocallyClosed(error);
}

protected void handleLinkLocallyClosed(ErrorCondition error) {
// Default does nothing.
}

@Override
Expand Down
Loading

0 comments on commit b58191b

Please sign in to comment.