This is an automated email from the ASF dual-hosted git repository.
sijie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-pulsar.git
The following commit(s) were added to refs/heads/master by this push:
new 233c77f Issue 1967: Non-persistent topic drop too many messages
(#1994)
233c77f is described below
commit 233c77fca1ac1bfbc439c2246a45673ff366bfc3
Author: Sijie Guo <[email protected]>
AuthorDate: Wed Jun 20 16:36:50 2018 -0700
Issue 1967: Non-persistent topic drop too many messages (#1994)
*Motivation*
Fixes #1967.
in 2.0 there is an optimization change on grouping acknowledgements.
The [optimization
change](https://github.com/apache/incubator-pulsar/commit/19dd2c502725e3b45e56541576508626c4213091#diff-debb36270f152d2533b03902f214fa9aR674)
avoided delivering messages that are already "delivered". However in
non-persistent topic, pulsar doesn't store entries to bookkeeper, so no ledger
id and entry id
are assigned for those messages. Pulsar uses 0 for both ledger id and
entry. So the optimization change treats messages as already "delivered". so
pulsar client
doesn't dispatch those messages even it already received them from broker.
*Changes*
- Make AcknowledgementsGroupingTracker an interface
- Introduce a no-op implementation for non-persistent topics
Signed-off-by: Sijie Guo <[email protected]>
---
.../impl/AcknowledgmentsGroupingTracker.java | 185 +--------------------
.../apache/pulsar/client/impl/ConsumerImpl.java | 11 +-
...NonPersistentAcknowledgmentGroupingTracker.java | 57 +++++++
... PersistentAcknowledgmentsGroupingTracker.java} | 11 +-
.../impl/AcknowledgementsGroupingTrackerTest.java | 6 +-
.../org/apache/pulsar/common/naming/TopicName.java | 4 +
6 files changed, 86 insertions(+), 188 deletions(-)
diff --git
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/AcknowledgmentsGroupingTracker.java
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/AcknowledgmentsGroupingTracker.java
index 68ab1fb..195a9bb 100644
---
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/AcknowledgmentsGroupingTracker.java
+++
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/AcknowledgmentsGroupingTracker.java
@@ -18,193 +18,22 @@
*/
package org.apache.pulsar.client.impl;
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.EventLoopGroup;
-
-import java.io.Closeable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import java.util.Map;
-import java.util.concurrent.ConcurrentSkipListSet;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
-
-import lombok.extern.slf4j.Slf4j;
-
-import org.apache.commons.lang3.tuple.Pair;
import org.apache.pulsar.client.api.MessageId;
-import org.apache.pulsar.client.impl.conf.ConsumerConfigurationData;
-import org.apache.pulsar.common.api.Commands;
import org.apache.pulsar.common.api.proto.PulsarApi.CommandAck.AckType;
/**
- * Group the acknowledgments for a certain time and then sends them out in a
single protobuf command.
+ * Acknowledgments grouping tracker.
*/
-@Slf4j
-public class AcknowledgmentsGroupingTracker implements Closeable {
-
- /**
- * When reaching the max group size, an ack command is sent out immediately
- */
- private static final int MAX_ACK_GROUP_SIZE = 1000;
-
- private final ConsumerImpl<?> consumer;
-
- private final long acknowledgementGroupTimeMicros;
-
- /**
- * Latest cumulative ack sent to broker
- */
- private volatile MessageIdImpl lastCumulativeAck = (MessageIdImpl)
MessageId.earliest;
-
- private static final
AtomicReferenceFieldUpdater<AcknowledgmentsGroupingTracker, MessageIdImpl>
LAST_CUMULATIVE_ACK_UPDATER = AtomicReferenceFieldUpdater
- .newUpdater(AcknowledgmentsGroupingTracker.class,
MessageIdImpl.class, "lastCumulativeAck");
-
- /**
- * This is a set of all the individual acks that the application has
issued and that were not already sent to
- * broker.
- */
- private final ConcurrentSkipListSet<MessageIdImpl> pendingIndividualAcks;
-
- private final ScheduledFuture<?> scheduledTask;
-
- public AcknowledgmentsGroupingTracker(ConsumerImpl<?> consumer,
ConsumerConfigurationData<?> conf,
- EventLoopGroup eventLoopGroup) {
- this.consumer = consumer;
- this.pendingIndividualAcks = new ConcurrentSkipListSet<>();
- this.acknowledgementGroupTimeMicros =
conf.getAcknowledgementsGroupTimeMicros();
-
- if (acknowledgementGroupTimeMicros > 0) {
- scheduledTask =
eventLoopGroup.next().scheduleWithFixedDelay(this::flush,
acknowledgementGroupTimeMicros,
- acknowledgementGroupTimeMicros, TimeUnit.MICROSECONDS);
- } else {
- scheduledTask = null;
- }
- }
-
- /**
- * Since the ack are delayed, we need to do some best-effort duplicate
check to discard messages that are being
- * resent after a disconnection and for which the user has already sent an
acknowlowdgement.
- */
- public boolean isDuplicate(MessageId messageId) {
- if (messageId.compareTo(lastCumulativeAck) <= 0) {
- // Already included in a cumulative ack
- return true;
- } else {
- return pendingIndividualAcks.contains(messageId);
- }
- }
+public interface AcknowledgmentsGroupingTracker extends AutoCloseable {
- public void addAcknowledgment(MessageIdImpl msgId, AckType ackType,
Map<String, Long> properties) {
- if (acknowledgementGroupTimeMicros == 0 || !properties.isEmpty()) {
- // We cannot group acks if the delay is 0 or when there are
properties attached to it. Fortunately that's an
- // uncommon condition since it's only used for the compaction
subscription.
- doImmediateAck(msgId, ackType, properties);
- } else if (ackType == AckType.Cumulative) {
- doCumulativeAck(msgId);
- } else {
- // Individual ack
- pendingIndividualAcks.add(msgId);
- if (pendingIndividualAcks.size() >= MAX_ACK_GROUP_SIZE) {
- flush();
- }
- }
- }
+ boolean isDuplicate(MessageId messageId);
- private void doCumulativeAck(MessageIdImpl msgId) {
- // Handle concurrent updates from different threads
- while (true) {
- MessageIdImpl lastCumlativeAck = this.lastCumulativeAck;
- if (msgId.compareTo(lastCumlativeAck) > 0) {
- if (LAST_CUMULATIVE_ACK_UPDATER.compareAndSet(this,
lastCumlativeAck, msgId)) {
- // Successfully updated the last cumlative ack. Next flush
iteration will send this to broker.
- return;
- }
- } else {
- // message id acknowledging an before the current last
cumulative ack
- return;
- }
- }
- }
+ void addAcknowledgment(MessageIdImpl msgId, AckType ackType, Map<String,
Long> properties);
- private boolean doImmediateAck(MessageIdImpl msgId, AckType ackType,
Map<String, Long> properties) {
- ClientCnx cnx = consumer.getClientCnx();
-
- if (cnx == null) {
- return false;
- }
-
- final ByteBuf cmd = Commands.newAck(consumer.consumerId,
msgId.getLedgerId(), msgId.getEntryId(), ackType, null,
- properties);
-
- cnx.ctx().writeAndFlush(cmd, cnx.ctx().voidPromise());
- return true;
- }
-
- /**
- * Flush all the pending acks and send them to the broker
- */
- public void flush() {
- if (log.isDebugEnabled()) {
- log.debug("[{}] Flushing pending acks to broker:
last-cumulative-ack: {} -- individual-acks: {}", consumer,
- lastCumulativeAck, pendingIndividualAcks);
- }
-
- ClientCnx cnx = consumer.getClientCnx();
-
- if (cnx == null) {
- if (log.isDebugEnabled()) {
- log.debug("[{}] Cannot flush pending acks since we're not
connected to broker", consumer);
- }
- return;
- }
-
- if (!lastCumulativeAck.equals(MessageId.earliest)) {
- ByteBuf cmd = Commands.newAck(consumer.consumerId,
lastCumulativeAck.ledgerId, lastCumulativeAck.entryId,
- AckType.Cumulative, null, Collections.emptyMap());
- cnx.ctx().write(cmd, cnx.ctx().voidPromise());
- }
-
- // Flush all individual acks
- if (!pendingIndividualAcks.isEmpty()) {
- if
(Commands.peerSupportsMultiMessageAcknowledgment(cnx.getRemoteEndpointProtocolVersion()))
{
- // We can send 1 single protobuf command with all individual
acks
- List<Pair<Long, Long>> entriesToAck = new
ArrayList<>(pendingIndividualAcks.size());
- while (true) {
- MessageIdImpl msgId = pendingIndividualAcks.pollFirst();
- if (msgId == null) {
- break;
- }
-
- entriesToAck.add(Pair.of(msgId.getLedgerId(),
msgId.getEntryId()));
- }
-
-
cnx.ctx().write(Commands.newMultiMessageAck(consumer.consumerId, entriesToAck),
- cnx.ctx().voidPromise());
- } else {
- // When talking to older brokers, send the acknowledgments
individually
- while (true) {
- MessageIdImpl msgId = pendingIndividualAcks.pollFirst();
- if (msgId == null) {
- break;
- }
-
- cnx.ctx().write(Commands.newAck(consumer.consumerId,
msgId.getLedgerId(), msgId.getEntryId(),
- AckType.Individual, null, Collections.emptyMap()),
cnx.ctx().voidPromise());
- }
- }
- }
-
- cnx.ctx().flush();
- }
+ void flush();
@Override
- public void close() {
- flush();
- if (scheduledTask != null) {
- scheduledTask.cancel(true);
- }
- }
+ void close();
+
}
diff --git
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/ConsumerImpl.java
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/ConsumerImpl.java
index dc98dc2..dd3b5c4 100644
---
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/ConsumerImpl.java
+++
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/ConsumerImpl.java
@@ -72,6 +72,7 @@ import
org.apache.pulsar.common.api.proto.PulsarApi.MessageMetadata;
import org.apache.pulsar.common.api.proto.PulsarApi.ProtocolVersion;
import org.apache.pulsar.common.compression.CompressionCodec;
import org.apache.pulsar.common.compression.CompressionCodecProvider;
+import org.apache.pulsar.common.naming.TopicName;
import org.apache.pulsar.common.util.FutureUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -152,7 +153,15 @@ public class ConsumerImpl<T> extends ConsumerBase<T>
implements ConnectionHandle
this.priorityLevel = conf.getPriorityLevel();
this.readCompacted = conf.isReadCompacted();
this.subscriptionInitialPosition =
conf.getSubscriptionInitialPosition();
- this.acknowledgmentsGroupingTracker = new
AcknowledgmentsGroupingTracker(this, conf, client.eventLoopGroup());
+
+ TopicName topicName = TopicName.get(topic);
+ if (topicName.isPersistent()) {
+ this.acknowledgmentsGroupingTracker =
+ new PersistentAcknowledgmentsGroupingTracker(this, conf,
client.eventLoopGroup());
+ } else {
+ this.acknowledgmentsGroupingTracker =
+ NonPersistentAcknowledgmentGroupingTracker.of();
+ }
if (client.getConfiguration().getStatsIntervalSeconds() > 0) {
stats = new ConsumerStatsRecorderImpl(client, conf, this);
diff --git
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/NonPersistentAcknowledgmentGroupingTracker.java
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/NonPersistentAcknowledgmentGroupingTracker.java
new file mode 100644
index 0000000..fbf6d03
--- /dev/null
+++
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/NonPersistentAcknowledgmentGroupingTracker.java
@@ -0,0 +1,57 @@
+/**
+ * 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.pulsar.client.impl;
+
+import java.util.Map;
+import org.apache.pulsar.client.api.MessageId;
+import org.apache.pulsar.common.api.proto.PulsarApi.CommandAck.AckType;
+
+/**
+ * A no-op acknowledgment grouping tracker.
+ */
+public class NonPersistentAcknowledgmentGroupingTracker implements
AcknowledgmentsGroupingTracker {
+
+ public static NonPersistentAcknowledgmentGroupingTracker of() {
+ return INSTANCE;
+ }
+
+ private static final NonPersistentAcknowledgmentGroupingTracker INSTANCE =
new NonPersistentAcknowledgmentGroupingTracker();
+
+ private NonPersistentAcknowledgmentGroupingTracker() {}
+
+ @Override
+ public boolean isDuplicate(MessageId messageId) {
+ return false;
+ }
+
+ @Override
+ public void addAcknowledgment(MessageIdImpl msgId, AckType ackType,
Map<String, Long> properties) {
+ // no-op
+ }
+
+ @Override
+ public void flush() {
+ // no-op
+ }
+
+ @Override
+ public void close() {
+ // no-op
+ }
+}
diff --git
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/AcknowledgmentsGroupingTracker.java
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/PersistentAcknowledgmentsGroupingTracker.java
similarity index 93%
copy from
pulsar-client/src/main/java/org/apache/pulsar/client/impl/AcknowledgmentsGroupingTracker.java
copy to
pulsar-client/src/main/java/org/apache/pulsar/client/impl/PersistentAcknowledgmentsGroupingTracker.java
index 68ab1fb..0fefe9c 100644
---
a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/AcknowledgmentsGroupingTracker.java
+++
b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/PersistentAcknowledgmentsGroupingTracker.java
@@ -21,7 +21,6 @@ package org.apache.pulsar.client.impl;
import io.netty.buffer.ByteBuf;
import io.netty.channel.EventLoopGroup;
-import java.io.Closeable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -43,7 +42,7 @@ import
org.apache.pulsar.common.api.proto.PulsarApi.CommandAck.AckType;
* Group the acknowledgments for a certain time and then sends them out in a
single protobuf command.
*/
@Slf4j
-public class AcknowledgmentsGroupingTracker implements Closeable {
+public class PersistentAcknowledgmentsGroupingTracker implements
AcknowledgmentsGroupingTracker {
/**
* When reaching the max group size, an ack command is sent out immediately
@@ -59,8 +58,8 @@ public class AcknowledgmentsGroupingTracker implements
Closeable {
*/
private volatile MessageIdImpl lastCumulativeAck = (MessageIdImpl)
MessageId.earliest;
- private static final
AtomicReferenceFieldUpdater<AcknowledgmentsGroupingTracker, MessageIdImpl>
LAST_CUMULATIVE_ACK_UPDATER = AtomicReferenceFieldUpdater
- .newUpdater(AcknowledgmentsGroupingTracker.class,
MessageIdImpl.class, "lastCumulativeAck");
+ private static final
AtomicReferenceFieldUpdater<PersistentAcknowledgmentsGroupingTracker,
MessageIdImpl> LAST_CUMULATIVE_ACK_UPDATER = AtomicReferenceFieldUpdater
+ .newUpdater(PersistentAcknowledgmentsGroupingTracker.class,
MessageIdImpl.class, "lastCumulativeAck");
/**
* This is a set of all the individual acks that the application has
issued and that were not already sent to
@@ -70,8 +69,8 @@ public class AcknowledgmentsGroupingTracker implements
Closeable {
private final ScheduledFuture<?> scheduledTask;
- public AcknowledgmentsGroupingTracker(ConsumerImpl<?> consumer,
ConsumerConfigurationData<?> conf,
- EventLoopGroup eventLoopGroup) {
+ public PersistentAcknowledgmentsGroupingTracker(ConsumerImpl<?> consumer,
ConsumerConfigurationData<?> conf,
+ EventLoopGroup
eventLoopGroup) {
this.consumer = consumer;
this.pendingIndividualAcks = new ConcurrentSkipListSet<>();
this.acknowledgementGroupTimeMicros =
conf.getAcknowledgementsGroupTimeMicros();
diff --git
a/pulsar-client/src/test/java/org/apache/pulsar/client/impl/AcknowledgementsGroupingTrackerTest.java
b/pulsar-client/src/test/java/org/apache/pulsar/client/impl/AcknowledgementsGroupingTrackerTest.java
index b6a3d2a..fc6df91 100644
---
a/pulsar-client/src/test/java/org/apache/pulsar/client/impl/AcknowledgementsGroupingTrackerTest.java
+++
b/pulsar-client/src/test/java/org/apache/pulsar/client/impl/AcknowledgementsGroupingTrackerTest.java
@@ -61,7 +61,7 @@ public class AcknowledgementsGroupingTrackerTest {
public void testAckTracker() throws Exception {
ConsumerConfigurationData<?> conf = new ConsumerConfigurationData<>();
conf.setAcknowledgementsGroupTimeMicros(TimeUnit.SECONDS.toMicros(10));
- AcknowledgmentsGroupingTracker tracker = new
AcknowledgmentsGroupingTracker(consumer, conf, eventLoopGroup);
+ PersistentAcknowledgmentsGroupingTracker tracker = new
PersistentAcknowledgmentsGroupingTracker(consumer, conf, eventLoopGroup);
MessageIdImpl msg1 = new MessageIdImpl(5, 1, 0);
MessageIdImpl msg2 = new MessageIdImpl(5, 2, 0);
@@ -119,7 +119,7 @@ public class AcknowledgementsGroupingTrackerTest {
public void testImmediateAckingTracker() throws Exception {
ConsumerConfigurationData<?> conf = new ConsumerConfigurationData<>();
conf.setAcknowledgementsGroupTimeMicros(0);
- AcknowledgmentsGroupingTracker tracker = new
AcknowledgmentsGroupingTracker(consumer, conf, eventLoopGroup);
+ PersistentAcknowledgmentsGroupingTracker tracker = new
PersistentAcknowledgmentsGroupingTracker(consumer, conf, eventLoopGroup);
MessageIdImpl msg1 = new MessageIdImpl(5, 1, 0);
MessageIdImpl msg2 = new MessageIdImpl(5, 2, 0);
@@ -146,7 +146,7 @@ public class AcknowledgementsGroupingTrackerTest {
public void testAckTrackerMultiAck() throws Exception {
ConsumerConfigurationData<?> conf = new ConsumerConfigurationData<>();
conf.setAcknowledgementsGroupTimeMicros(TimeUnit.SECONDS.toMicros(10));
- AcknowledgmentsGroupingTracker tracker = new
AcknowledgmentsGroupingTracker(consumer, conf, eventLoopGroup);
+ PersistentAcknowledgmentsGroupingTracker tracker = new
PersistentAcknowledgmentsGroupingTracker(consumer, conf, eventLoopGroup);
when(cnx.getRemoteEndpointProtocolVersion()).thenReturn(ProtocolVersion.v12_VALUE);
diff --git
a/pulsar-common/src/main/java/org/apache/pulsar/common/naming/TopicName.java
b/pulsar-common/src/main/java/org/apache/pulsar/common/naming/TopicName.java
index ba5da90..ef45598 100644
--- a/pulsar-common/src/main/java/org/apache/pulsar/common/naming/TopicName.java
+++ b/pulsar-common/src/main/java/org/apache/pulsar/common/naming/TopicName.java
@@ -170,6 +170,10 @@ public class TopicName implements ServiceUnitId {
}
+ public boolean isPersistent() {
+ return TopicDomain.persistent == domain;
+ }
+
/**
* Extract the namespace portion out of a completeTopicName name.
*