Skip to content

Commit 82a54aa

Browse files
authored
Log tracking of ByteBuf require-release operations (moquette-io#869)
Decorates require and release with logging and tag to track the chainof retaining of a buffer
1 parent 0d22c0d commit 82a54aa

14 files changed

+160
-53
lines changed

broker/src/main/java/io/moquette/broker/InMemoryQueue.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public boolean isEmpty() {
4242
@Override
4343
public void closeAndPurge() {
4444
for (SessionRegistry.EnqueuedMessage msg : queue) {
45-
msg.release();
45+
Utils.release(msg, "in memory queue cleanup");
4646
}
4747
if (queueRepository != null) {
4848
// clean up the queue from the repository

broker/src/main/java/io/moquette/broker/MQTTConnection.java

+8-5
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ PostOffice.RouteResult processPublish(MqttPublishMessage msg) {
682682

683683
// retain else msg is cleaned by the NewNettyMQTTHandler and is not available
684684
// in execution by SessionEventLoop
685-
msg.retain();
685+
Utils.retain(msg, PostOffice.BT_PUB_IN);
686686
switch (qos) {
687687
case AT_MOST_ONCE:
688688
return postOffice.routeCommand(clientId, "PUB QoS0", () -> {
@@ -692,10 +692,11 @@ PostOffice.RouteResult processPublish(MqttPublishMessage msg) {
692692
}
693693
postOffice.receivedPublishQos0(this, username, clientId, msg, expiry);
694694
return null;
695-
}).ifFailed(msg::release);
695+
}).ifFailed(() -> Utils.release(msg, PostOffice.BT_PUB_IN + " - failed"));
696696
case AT_LEAST_ONCE:
697697
if (!receivedQuota.hasFreeSlots()) {
698698
LOG.warn("Client {} exceeded the quota {} processing QoS1, disconnecting it", clientId, receivedQuota);
699+
Utils.release(msg, PostOffice.BT_PUB_IN + " - QoS1 exceeded quota");
699700
brokerDisconnect(MqttReasonCodes.Disconnect.RECEIVE_MAXIMUM_EXCEEDED);
700701
disconnectSession();
701702
dropConnection();
@@ -712,10 +713,11 @@ PostOffice.RouteResult processPublish(MqttPublishMessage msg) {
712713
receivedQuota.releaseSlot();
713714
});
714715
return null;
715-
}).ifFailed(msg::release);
716+
}).ifFailed(() -> Utils.release(msg, PostOffice.BT_PUB_IN + " - failed"));
716717
case EXACTLY_ONCE: {
717718
if (!receivedQuota.hasFreeSlots()) {
718719
LOG.warn("Client {} exceeded the quota {} processing QoS2, disconnecting it", clientId, receivedQuota);
720+
Utils.release(msg, PostOffice.BT_PUB_IN + " - phase 1 QoS2 exceeded quota");
719721
brokerDisconnect(MqttReasonCodes.Disconnect.RECEIVE_MAXIMUM_EXCEEDED);
720722
disconnectSession();
721723
dropConnection();
@@ -731,7 +733,7 @@ PostOffice.RouteResult processPublish(MqttPublishMessage msg) {
731733
return null;
732734
});
733735
if (!firstStepResult.isSuccess()) {
734-
msg.release();
736+
Utils.release(msg, PostOffice.BT_PUB_IN + " - failed");
735737
LOG.trace("Failed to enqueue PUB QoS2 to session loop for {}", clientId);
736738
return firstStepResult;
737739
}
@@ -816,9 +818,10 @@ void sendIfWritableElseDrop(MqttMessage msg) {
816818
LOG.debug("Sending message {} on the wire to {}", msg.fixedHeader().messageType(), getClientId());
817819
// Sending to external, retain a duplicate. Just retain is not
818820
// enough, since the receiver must have full control.
821+
// Retain because the OutboundHandler does a release of the buffer.
819822
Object retainedDup = msg;
820823
if (msg instanceof ByteBufHolder) {
821-
retainedDup = ((ByteBufHolder) msg).retainedDuplicate();
824+
retainedDup = Utils.retainDuplicate((ByteBufHolder) msg, "mqtt connection send PUB");
822825
}
823826

824827
ChannelFuture channelFuture;

broker/src/main/java/io/moquette/broker/NewNettyAcceptor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ protected void decode(ChannelHandlerContext chc, BinaryWebSocketFrame frame, Lis
7777
ByteBuf bb = frame.content();
7878
// System.out.println("WebSocketFrameToByteBufDecoder decode - " +
7979
// ByteBufUtil.hexDump(bb));
80-
bb.retain();
80+
Utils.retain(bb, "Websocket decoder");
8181
out.add(bb);
8282
}
8383
}

broker/src/main/java/io/moquette/broker/NewNettyMQTTHandler.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public void operationComplete(ChannelFuture future) {
6666
}
6767
});
6868
} finally {
69-
ReferenceCountUtil.release(msg);
69+
Utils.release(msg, "payload in - channel handler, read");
7070
}
7171
}
7272

broker/src/main/java/io/moquette/broker/PostOffice.java

+23-15
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ class PostOffice {
6767

6868
private static final String WILL_PUBLISHER = "will_publisher";
6969
private static final String INTERNAL_PUBLISHER = "internal_publisher";
70+
public static final String BT_ROUTE_TARGET = "Route to target session";
71+
public static final String BT_PUB_IN = "PUB in";
7072

7173
/**
7274
* Maps the failed packetID per clientId (id client source, id_packet) -> [id client target]
@@ -621,14 +623,14 @@ CompletableFuture<Void> receivedPublishQos0(MQTTConnection connection, String us
621623
final Topic topic = new Topic(msg.variableHeader().topicName());
622624
if (!authorizator.canWrite(topic, username, clientID)) {
623625
LOG.error("client is not authorized to publish on topic: {}", topic);
624-
ReferenceCountUtil.release(msg);
626+
Utils.release(msg,PostOffice.BT_PUB_IN + " - ok, auth failed");
625627
return CompletableFuture.completedFuture(null);
626628
}
627629

628630
if (isPayloadFormatToValidate(msg)) {
629631
if (!validatePayloadAsUTF8(msg)) {
630632
LOG.warn("Received not valid UTF-8 payload when payload format indicator was enabled (QoS0)");
631-
ReferenceCountUtil.release(msg);
633+
Utils.release(msg,PostOffice.BT_PUB_IN + " - ok, invalid format");
632634
connection.brokerDisconnect(MqttReasonCodes.Disconnect.PAYLOAD_FORMAT_INVALID);
633635
connection.disconnectSession();
634636
connection.dropConnection();
@@ -639,7 +641,7 @@ CompletableFuture<Void> receivedPublishQos0(MQTTConnection connection, String us
639641
final RoutingResults publishResult = publish2Subscribers(clientID, messageExpiry, msg);
640642
if (publishResult.isAllFailed()) {
641643
LOG.info("No one publish was successfully enqueued to session loops");
642-
ReferenceCountUtil.release(msg);
644+
Utils.release(msg,PostOffice.BT_PUB_IN + " - ok, can't forward to next session loop");
643645
return CompletableFuture.completedFuture(null);
644646
}
645647

@@ -650,7 +652,7 @@ CompletableFuture<Void> receivedPublishQos0(MQTTConnection connection, String us
650652
}
651653

652654
interceptor.notifyTopicPublished(msg, clientID, username);
653-
ReferenceCountUtil.release(msg);
655+
Utils.release(msg,PostOffice.BT_PUB_IN + " - ok");
654656
});
655657
}
656658

@@ -662,13 +664,13 @@ RoutingResults receivedPublishQos1(MQTTConnection connection, String username, i
662664
if (!topic.isValid()) {
663665
LOG.warn("Invalid topic format, force close the connection");
664666
connection.dropConnection();
665-
ReferenceCountUtil.release(msg);
667+
Utils.release(msg,PostOffice.BT_PUB_IN + " - ok, qos1 invalid topic");
666668
return RoutingResults.preroutingError();
667669
}
668670
final String clientId = connection.getClientId();
669671
if (!authorizator.canWrite(topic, username, clientId)) {
670672
LOG.error("MQTT client: {} is not authorized to publish on topic: {}", clientId, topic);
671-
ReferenceCountUtil.release(msg);
673+
Utils.release(msg,PostOffice.BT_PUB_IN + " - ok, qos1 auth failed");
672674
return RoutingResults.preroutingError();
673675
}
674676

@@ -677,15 +679,15 @@ RoutingResults receivedPublishQos1(MQTTConnection connection, String username, i
677679
LOG.warn("Received not valid UTF-8 payload when payload format indicator was enabled (QoS1)");
678680
connection.sendPubAck(messageID, MqttReasonCodes.PubAck.PAYLOAD_FORMAT_INVALID);
679681

680-
ReferenceCountUtil.release(msg);
682+
Utils.release(msg,PostOffice.BT_PUB_IN + " - ok, qos1 invalid format");
681683
return RoutingResults.preroutingError();
682684
}
683685
}
684686

685687
if (isContentTypeToValidate(msg)) {
686688
if (!validateContentTypeAsUTF8(msg)) {
687689
LOG.warn("Received not valid UTF-8 content type (QoS1)");
688-
ReferenceCountUtil.release(msg);
690+
Utils.release(msg,PostOffice.BT_PUB_IN + " - ok, qos1 invalid content type");
689691
connection.brokerDisconnect(MqttReasonCodes.Disconnect.PROTOCOL_ERROR);
690692
connection.disconnectSession();
691693
connection.dropConnection();
@@ -713,7 +715,7 @@ RoutingResults receivedPublishQos1(MQTTConnection connection, String username, i
713715
// some session event loop enqueue raised a problem
714716
failedPublishes.insertAll(messageID, clientId, routes.failedRoutings);
715717
}
716-
ReferenceCountUtil.release(msg);
718+
Utils.release(msg,PostOffice.BT_PUB_IN + " - ok, qos1");
717719

718720
// cleanup success resends from the failed publishes cache
719721
failedPublishes.removeAll(messageID, clientId, routes.successedRoutings);
@@ -871,12 +873,18 @@ private RoutingResults publish2Subscribers(String publisherClientId,
871873
LOG.trace("No matching subscriptions for topic: {}", topic);
872874
return new RoutingResults(Collections.emptyList(), Collections.emptyList(), CompletableFuture.completedFuture(null));
873875
}
876+
// sanity check
877+
if (subscriptionCount > sessionLoops.getEventLoopCount()) {
878+
LOG.error("Cardinality of subscription batches ({}) is bigger then the available session loops {}",
879+
subscriptionCount, sessionLoops.getEventLoopCount());
880+
return new RoutingResults(Collections.emptyList(), Collections.emptyList(), CompletableFuture.completedFuture(null));
881+
}
874882

875-
msg.retain(subscriptionCount);
883+
Utils.retain(msg, subscriptionCount, BT_ROUTE_TARGET);
876884

877885
List<RouteResult> publishResults = collector.routeBatchedPublishes((batch) -> {
878886
publishToSession(topic, batch, publishingQos, retainPublish, messageExpiry, msg);
879-
msg.release();
887+
Utils.release(msg, BT_ROUTE_TARGET);
880888
});
881889

882890
final CompletableFuture[] publishFutures = publishResults.stream()
@@ -890,7 +898,7 @@ private RoutingResults publish2Subscribers(String publisherClientId,
890898
Collection<String> subscibersIds = collector.subscriberIdsByEventLoop(rr.clientId);
891899
if (rr.status == RouteResult.Status.FAIL) {
892900
failedRoutings.addAll(subscibersIds);
893-
msg.release();
901+
Utils.release(msg, BT_ROUTE_TARGET + "- failed routing");
894902
} else {
895903
successedRoutings.addAll(subscibersIds);
896904
}
@@ -970,7 +978,7 @@ RoutingResults receivedPublishQos2(MQTTConnection connection, MqttPublishMessage
970978
final String clientId = connection.getClientId();
971979
if (!authorizator.canWrite(topic, username, clientId)) {
972980
LOG.error("MQTT client is not authorized to publish on topic: {}", topic);
973-
ReferenceCountUtil.release(msg);
981+
Utils.release(msg,PostOffice.BT_PUB_IN + " - ok, phase 2 qos2 auth failed");
974982
// WARN this is a special case failed is empty, but this result is to be considered as error.
975983
return RoutingResults.preroutingError();
976984
}
@@ -981,7 +989,7 @@ RoutingResults receivedPublishQos2(MQTTConnection connection, MqttPublishMessage
981989
LOG.warn("Received not valid UTF-8 payload when payload format indicator was enabled (QoS2)");
982990
connection.sendPubRec(messageID, MqttReasonCodes.PubRec.PAYLOAD_FORMAT_INVALID);
983991

984-
ReferenceCountUtil.release(msg);
992+
Utils.release(msg,PostOffice.BT_PUB_IN + " - ok, phase 2 qos2 invalid format");
985993
return RoutingResults.preroutingError();
986994
}
987995
}
@@ -1002,7 +1010,7 @@ RoutingResults receivedPublishQos2(MQTTConnection connection, MqttPublishMessage
10021010
// some session event loop enqueue raised a problem
10031011
failedPublishes.insertAll(messageID, clientId, publishRoutings.failedRoutings);
10041012
}
1005-
ReferenceCountUtil.release(msg);
1013+
Utils.release(msg,PostOffice.BT_PUB_IN + " - ok, phase 2 qos2");
10061014

10071015
// cleanup success resends from the failed publishes cache
10081016
failedPublishes.removeAll(messageID, clientId, publishRoutings.successedRoutings);

broker/src/main/java/io/moquette/broker/Server.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ public RoutingResults internalPublish(MqttPublishMessage msg, final String clien
592592
}
593593
LOG.trace("Internal publishing message CId: {}, messageId: {}", clientId, messageID);
594594
final RoutingResults routingResults = dispatcher.internalPublish(msg);
595-
msg.payload().release();
595+
Utils.release(msg, "Routing - internal PUB");
596596
return routingResults;
597597
}
598598

broker/src/main/java/io/moquette/broker/Session.java

+11-11
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ public void processPubRec(int pubRecPacketId) {
198198
LOG.warn("Received a PUBREC with not matching packetId");
199199
return;
200200
}
201-
removed.release();
201+
Utils.release(removed, "target session - phase 1 Qos2 pull from inflight");
202202
if (removed instanceof SessionRegistry.PubRelMarker) {
203203
LOG.info("Received a PUBREC for packetId that was already moved in second step of Qos2");
204204
return;
@@ -229,7 +229,7 @@ public void processPubComp(int messageID) {
229229
LOG.warn("Received a PUBCOMP with not matching packetId in the inflight cache");
230230
return;
231231
}
232-
removed.release();
232+
Utils.release(removed, "target session - phase 2 Qos2 pull from inflight");
233233
mqttConnection.sendQuota().releaseSlot();
234234
drainQueueToConnection();
235235

@@ -302,7 +302,7 @@ private void sendPublishQos2(PublishedMessage publishRequest) {
302302
private void sendPublishInFlightWindowOrQueueing(MQTTConnection localMqttConnectionRef,
303303
PublishedMessage publishRequest) {
304304
// retain the payload because it's going to be added to map or to the queue.
305-
publishRequest.retain();
305+
Utils.retain(publishRequest, "target session - forward to inflight or queue");
306306

307307
if (canSkipQueue(localMqttConnectionRef)) {
308308
mqttConnection.sendQuota().consumeSlot();
@@ -313,7 +313,7 @@ private void sendPublishInFlightWindowOrQueueing(MQTTConnection localMqttConnect
313313
EnqueuedMessage old = inflightWindow.put(packetId, publishRequest);
314314
// If there already was something, release it.
315315
if (old != null) {
316-
old.release();
316+
Utils.release(old, "target session - replace existing slot");
317317
mqttConnection.sendQuota().releaseSlot();
318318
}
319319
if (resendInflightOnTimeout) {
@@ -356,7 +356,7 @@ void pubAckReceived(int ackPacketId) {
356356
ackPacketId, inflightWindow.keySet());
357357
return;
358358
}
359-
removed.release();
359+
Utils.release(removed, "target session - inflight remove");
360360

361361
mqttConnection.sendQuota().releaseSlot();
362362
LOG.debug("Received PUBACK {} for session {}", ackPacketId, getClientID());
@@ -476,7 +476,7 @@ private void drainQueueToConnection() {
476476
// Putting it in a map, but the retain is cancelled out by the below release.
477477
EnqueuedMessage old = inflightWindow.put(sendPacketId, msg);
478478
if (old != null) {
479-
old.release();
479+
Utils.release(old, "target session - drain queue push to inflight");
480480
mqttConnection.sendQuota().releaseSlot();
481481
}
482482
if (resendInflightOnTimeout) {
@@ -515,19 +515,19 @@ public void reconnectSession() {
515515

516516
public void receivedPublishQos2(int messageID, MqttPublishMessage msg) {
517517
// Retain before putting msg in map.
518-
ReferenceCountUtil.retain(msg);
518+
Utils.retain(msg, "phase 2 qos2");
519519

520520
MqttPublishMessage old = qos2Receiving.put(messageID, msg);
521521
// In case of evil client with duplicate msgid.
522-
ReferenceCountUtil.release(old);
522+
Utils.release(old, "phase 2 qos2 - packet id duplicated");
523523

524524
// mqttConnection.sendPublishReceived(messageID);
525525
}
526526

527527
public void receivedPubRelQos2(int messageID) {
528528
// Done with the message, remove from queue and release payload.
529529
final MqttPublishMessage removedMsg = qos2Receiving.remove(messageID);
530-
ReferenceCountUtil.release(removedMsg);
530+
Utils.release(removedMsg, "phase 2 qos2");
531531
}
532532

533533
Optional<InetSocketAddress> remoteAddress() {
@@ -543,10 +543,10 @@ public void cleanUp() {
543543
sessionQueue.closeAndPurge();
544544
inflightTimeouts.clear();
545545
for (EnqueuedMessage msg : inflightWindow.values()) {
546-
msg.release();
546+
Utils.release(msg, "session cleanup - inflight window");
547547
}
548548
for (MqttPublishMessage msg : qos2Receiving.values()) {
549-
msg.release();
549+
Utils.release(msg, "session cleanup - phase 2 cache");
550550
}
551551
}
552552

broker/src/main/java/io/moquette/broker/SessionEventLoop.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,27 @@ final class SessionEventLoop extends Thread {
1010

1111
private static final Logger LOG = LoggerFactory.getLogger(SessionEventLoop.class);
1212

13-
private final BlockingQueue<FutureTask<String>> sessionQueue;
13+
private final BlockingQueue<FutureTask<String>> taskQueue;
1414
private final boolean flushOnExit;
1515

16-
public SessionEventLoop(BlockingQueue<FutureTask<String>> sessionQueue) {
17-
this(sessionQueue, true);
16+
public SessionEventLoop(BlockingQueue<FutureTask<String>> taskQueue) {
17+
this(taskQueue, true);
1818
}
1919

2020
/**
2121
* @param flushOnExit consume the commands queue before exit.
2222
* */
23-
public SessionEventLoop(BlockingQueue<FutureTask<String>> sessionQueue, boolean flushOnExit) {
24-
this.sessionQueue = sessionQueue;
23+
public SessionEventLoop(BlockingQueue<FutureTask<String>> taskQueue, boolean flushOnExit) {
24+
this.taskQueue = taskQueue;
2525
this.flushOnExit = flushOnExit;
2626
}
2727

2828
@Override
2929
public void run() {
30-
while (!Thread.interrupted() || (Thread.interrupted() && !sessionQueue.isEmpty() && flushOnExit)) {
30+
while (!Thread.interrupted() || (Thread.interrupted() && !taskQueue.isEmpty() && flushOnExit)) {
3131
try {
3232
// blocking call
33-
final FutureTask<String> task = this.sessionQueue.take();
33+
final FutureTask<String> task = this.taskQueue.take();
3434
executeTask(task);
3535
} catch (InterruptedException e) {
3636
LOG.info("SessionEventLoop {} interrupted", Thread.currentThread().getName());

0 commit comments

Comments
 (0)