This is an automated email from the ASF dual-hosted git repository.

mmerli 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 6b84809  Cpp client: add seek support in consumer for cpp client 
(#1863)
6b84809 is described below

commit 6b84809406d52c26cf7faa1a5543db209b803cc3
Author: Jia Zhai <[email protected]>
AuthorDate: Tue Jun 5 13:30:07 2018 +0800

    Cpp client: add seek support in consumer for cpp client (#1863)
    
    * add seek support in consumer for cpp client
    
    * add c wrapper
---
 pulsar-client-cpp/include/pulsar/Consumer.h      | 24 +++++++++
 pulsar-client-cpp/include/pulsar/c/consumer.h    |  5 ++
 pulsar-client-cpp/lib/Commands.cc                | 13 +++++
 pulsar-client-cpp/lib/Commands.h                 |  2 +
 pulsar-client-cpp/lib/Consumer.cc                | 21 ++++++++
 pulsar-client-cpp/lib/ConsumerImpl.cc            | 40 +++++++++++++++
 pulsar-client-cpp/lib/ConsumerImpl.h             |  2 +
 pulsar-client-cpp/lib/ConsumerImplBase.h         |  1 +
 pulsar-client-cpp/lib/PartitionedConsumerImpl.cc |  5 ++
 pulsar-client-cpp/lib/PartitionedConsumerImpl.h  |  1 +
 pulsar-client-cpp/lib/c/c_Consumer.cc            | 10 ++++
 pulsar-client-cpp/tests/BasicEndToEndTest.cc     | 65 ++++++++++++++++++++++++
 12 files changed, 189 insertions(+)

diff --git a/pulsar-client-cpp/include/pulsar/Consumer.h 
b/pulsar-client-cpp/include/pulsar/Consumer.h
index ff222ee..4272166 100644
--- a/pulsar-client-cpp/include/pulsar/Consumer.h
+++ b/pulsar-client-cpp/include/pulsar/Consumer.h
@@ -211,6 +211,30 @@ class Consumer {
      */
     void getBrokerConsumerStatsAsync(BrokerConsumerStatsCallback callback);
 
+    /**
+     * Reset the subscription associated with this consumer to a specific 
message id.
+     * The message id can either be a specific message or represent the first 
or last messages in the topic.
+     *
+     * Note: this operation can only be done on non-partitioned topics. For 
these, one can rather perform the
+     * seek() on the individual partitions.
+     *
+     * @param messageId
+     *            the message id where to reposition the subscription
+     */
+    Result seek(const MessageId& msgId);
+
+    /**
+     * Asynchronously reset the subscription associated with this consumer to 
a specific message id.
+     * The message id can either be a specific message or represent the first 
or last messages in the topic.
+     *
+     * Note: this operation can only be done on non-partitioned topics. For 
these, one can rather perform the
+     * seek() on the individual partitions.
+     *
+     * @param messageId
+     *            the message id where to reposition the subscription
+     */
+    virtual void seekAsync(const MessageId& msgId, ResultCallback callback);
+
    private:
     typedef boost::shared_ptr<ConsumerImplBase> ConsumerImplBasePtr;
     ConsumerImplBasePtr impl_;
diff --git a/pulsar-client-cpp/include/pulsar/c/consumer.h 
b/pulsar-client-cpp/include/pulsar/c/consumer.h
index e3e683b..f350ee0 100644
--- a/pulsar-client-cpp/include/pulsar/c/consumer.h
+++ b/pulsar-client-cpp/include/pulsar/c/consumer.h
@@ -192,6 +192,11 @@ pulsar_result resume_message_listener(pulsar_consumer_t 
*consumer);
  */
 void pulsar_consumer_redeliver_unacknowledged_messages(pulsar_consumer_t 
*consumer);
 
+void pulsar_consumer_seek_async(pulsar_consumer_t *consumer, 
pulsar_message_id_t *messageId,
+                                pulsar_result_callback callback, void *ctx);
+
+pulsar_result pulsar_consumer_seek(pulsar_consumer_t *consumer, 
pulsar_message_id_t *messageId);
+
 #pragma GCC visibility pop
 
 #ifdef __cplusplus
diff --git a/pulsar-client-cpp/lib/Commands.cc 
b/pulsar-client-cpp/lib/Commands.cc
index 27297b5..677fce0 100644
--- a/pulsar-client-cpp/lib/Commands.cc
+++ b/pulsar-client-cpp/lib/Commands.cc
@@ -298,6 +298,19 @@ SharedBuffer 
Commands::newRedeliverUnacknowledgedMessages(uint64_t consumerId) {
     return writeMessageWithSize(cmd);
 }
 
+SharedBuffer Commands::newSeek(uint64_t consumerId, uint64_t requestId, const 
MessageId& messageId) {
+    BaseCommand cmd;
+    cmd.set_type(BaseCommand::SEEK);
+    CommandSeek* commandSeek = cmd.mutable_seek();
+    commandSeek->set_consumer_id(consumerId);
+    commandSeek->set_request_id(requestId);
+
+    MessageIdData& messageIdData = *commandSeek->mutable_message_id();
+    messageIdData.set_ledgerid(messageId.ledgerId());
+    messageIdData.set_entryid(messageId.entryId());
+    return writeMessageWithSize(cmd);
+}
+
 std::string Commands::messageType(BaseCommand_Type type) {
     switch (type) {
         case BaseCommand::CONNECT:
diff --git a/pulsar-client-cpp/lib/Commands.h b/pulsar-client-cpp/lib/Commands.h
index 3746dc5..38c498a 100644
--- a/pulsar-client-cpp/lib/Commands.h
+++ b/pulsar-client-cpp/lib/Commands.h
@@ -109,6 +109,8 @@ class Commands {
 
     static SharedBuffer newConsumerStats(uint64_t consumerId, uint64_t 
requestId);
 
+    static SharedBuffer newSeek(uint64_t consumerId, uint64_t requestId, const 
MessageId& messageId);
+
    private:
     Commands();
 
diff --git a/pulsar-client-cpp/lib/Consumer.cc 
b/pulsar-client-cpp/lib/Consumer.cc
index 726676d..d89dd73 100644
--- a/pulsar-client-cpp/lib/Consumer.cc
+++ b/pulsar-client-cpp/lib/Consumer.cc
@@ -189,4 +189,25 @@ void 
Consumer::getBrokerConsumerStatsAsync(BrokerConsumerStatsCallback callback)
     }
     impl_->getBrokerConsumerStatsAsync(callback);
 }
+
+void Consumer::seekAsync(const MessageId& msgId, ResultCallback callback) {
+    if (!impl_) {
+        callback(ResultConsumerNotInitialized);
+        return;
+    }
+    impl_->seekAsync(msgId, callback);
+}
+
+Result Consumer::seek(const MessageId& msgId) {
+    if (!impl_) {
+        return ResultConsumerNotInitialized;
+    }
+
+    Promise<bool, Result> promise;
+    impl_->seekAsync(msgId, WaitForCallback(promise));
+    Result result;
+    promise.getFuture().get(result);
+    return result;
+}
+
 }  // namespace pulsar
diff --git a/pulsar-client-cpp/lib/ConsumerImpl.cc 
b/pulsar-client-cpp/lib/ConsumerImpl.cc
index 2abbdd0..0425238 100644
--- a/pulsar-client-cpp/lib/ConsumerImpl.cc
+++ b/pulsar-client-cpp/lib/ConsumerImpl.cc
@@ -879,4 +879,44 @@ void ConsumerImpl::brokerConsumerStatsListener(Result res, 
BrokerConsumerStatsIm
     }
 }
 
+void ConsumerImpl::handleSeek(Result result, ResultCallback callback) {
+    if (result == ResultOk) {
+        LOG_INFO(getName() << "Seek successfully");
+    } else {
+        LOG_ERROR(getName() << "Failed to seek: " << strResult(result));
+    }
+    callback(result);
+}
+
+void ConsumerImpl::seekAsync(const MessageId& msgId, ResultCallback callback) {
+    Lock lock(mutex_);
+    if (state_ == Closed || state_ == Closing) {
+        lock.unlock();
+        LOG_ERROR(getName() << "Client connection already closed.");
+        if (!callback.empty()) {
+            callback(ResultAlreadyClosed);
+        }
+        return;
+    }
+    lock.unlock();
+
+    ClientConnectionPtr cnx = getCnx().lock();
+    if (cnx) {
+        ClientImplPtr client = client_.lock();
+        uint64_t requestId = client->newRequestId();
+        LOG_DEBUG(getName() << " Sending seek Command for Consumer - " << 
getConsumerId() << ", requestId - "
+                            << requestId);
+        Future<Result, ResponseData> future =
+            cnx->sendRequestWithId(Commands::newSeek(consumerId_, requestId, 
msgId), requestId);
+
+        if (!callback.empty()) {
+            future.addListener(boost::bind(&ConsumerImpl::handleSeek, 
shared_from_this(), _1, callback));
+        }
+        return;
+    }
+
+    LOG_ERROR(getName() << " Client Connection not ready for Consumer");
+    callback(ResultNotConnected);
+}
+
 } /* namespace pulsar */
diff --git a/pulsar-client-cpp/lib/ConsumerImpl.h 
b/pulsar-client-cpp/lib/ConsumerImpl.h
index 45a12ef..e35f444 100644
--- a/pulsar-client-cpp/lib/ConsumerImpl.h
+++ b/pulsar-client-cpp/lib/ConsumerImpl.h
@@ -102,6 +102,8 @@ class ConsumerImpl : public ConsumerImplBase,
     virtual Result resumeMessageListener();
     virtual void redeliverUnacknowledgedMessages();
     virtual void getBrokerConsumerStatsAsync(BrokerConsumerStatsCallback 
callback);
+    void handleSeek(Result result, ResultCallback callback);
+    virtual void seekAsync(const MessageId& msgId, ResultCallback callback);
 
    protected:
     void connectionOpened(const ClientConnectionPtr& cnx);
diff --git a/pulsar-client-cpp/lib/ConsumerImplBase.h 
b/pulsar-client-cpp/lib/ConsumerImplBase.h
index a1f3bd5..11f2fc6 100644
--- a/pulsar-client-cpp/lib/ConsumerImplBase.h
+++ b/pulsar-client-cpp/lib/ConsumerImplBase.h
@@ -49,6 +49,7 @@ class ConsumerImplBase {
     virtual const std::string& getName() const = 0;
     virtual int getNumOfPrefetchedMessages() const = 0;
     virtual void getBrokerConsumerStatsAsync(BrokerConsumerStatsCallback 
callback) = 0;
+    virtual void seekAsync(const MessageId& msgId, ResultCallback callback) = 
0;
 };
 }  // namespace pulsar
 #endif  // PULSAR_CONSUMER_IMPL_BASE_HEADER
diff --git a/pulsar-client-cpp/lib/PartitionedConsumerImpl.cc 
b/pulsar-client-cpp/lib/PartitionedConsumerImpl.cc
index dc218ef..199edb2 100644
--- a/pulsar-client-cpp/lib/PartitionedConsumerImpl.cc
+++ b/pulsar-client-cpp/lib/PartitionedConsumerImpl.cc
@@ -407,4 +407,9 @@ void PartitionedConsumerImpl::handleGetConsumerStats(Result 
res, BrokerConsumerS
         callback(ResultOk, BrokerConsumerStats(statsPtr));
     }
 }
+
+void PartitionedConsumerImpl::seekAsync(const MessageId& msgId, ResultCallback 
callback) {
+    callback(ResultOperationNotSupported);
+}
+
 }  // namespace pulsar
diff --git a/pulsar-client-cpp/lib/PartitionedConsumerImpl.h 
b/pulsar-client-cpp/lib/PartitionedConsumerImpl.h
index 56d04205..606c007 100644
--- a/pulsar-client-cpp/lib/PartitionedConsumerImpl.h
+++ b/pulsar-client-cpp/lib/PartitionedConsumerImpl.h
@@ -68,6 +68,7 @@ class PartitionedConsumerImpl : public ConsumerImplBase,
     virtual void getBrokerConsumerStatsAsync(BrokerConsumerStatsCallback 
callback);
     void handleGetConsumerStats(Result, BrokerConsumerStats, LatchPtr, 
PartitionedBrokerConsumerStatsPtr,
                                 size_t, BrokerConsumerStatsCallback);
+    virtual void seekAsync(const MessageId& msgId, ResultCallback callback);
 
    private:
     const ClientImplPtr client_;
diff --git a/pulsar-client-cpp/lib/c/c_Consumer.cc 
b/pulsar-client-cpp/lib/c/c_Consumer.cc
index dae824a..f9a211d 100644
--- a/pulsar-client-cpp/lib/c/c_Consumer.cc
+++ b/pulsar-client-cpp/lib/c/c_Consumer.cc
@@ -122,3 +122,13 @@ pulsar_result resume_message_listener(pulsar_consumer_t 
*consumer) {
 void pulsar_consumer_redeliver_unacknowledged_messages(pulsar_consumer_t 
*consumer) {
     return consumer->consumer.redeliverUnacknowledgedMessages();
 }
+
+void pulsar_consumer_seek_async(pulsar_consumer_t *consumer, 
pulsar_message_id_t *messageId,
+                                pulsar_result_callback callback, void *ctx) {
+    consumer->consumer.seekAsync(messageId->messageId,
+                                 boost::bind(handle_result_callback, _1, 
callback, ctx));
+}
+
+pulsar_result pulsar_consumer_seek(pulsar_consumer_t *consumer, 
pulsar_message_id_t *messageId) {
+    return (pulsar_result)consumer->consumer.seek(messageId->messageId);
+}
diff --git a/pulsar-client-cpp/tests/BasicEndToEndTest.cc 
b/pulsar-client-cpp/tests/BasicEndToEndTest.cc
index f9a6592..47713e9 100644
--- a/pulsar-client-cpp/tests/BasicEndToEndTest.cc
+++ b/pulsar-client-cpp/tests/BasicEndToEndTest.cc
@@ -1329,3 +1329,68 @@ TEST(BasicEndToEndTest, testEventTime) {
     consumer.close();
     producer.close();
 }
+
+TEST(BasicEndToEndTest, testSeek) {
+    ClientConfiguration config;
+    Client client(lookupUrl);
+    std::string topicName = "persistent://prop/unit/ns1/testSeek";
+    std::string subName = "sub-testSeek";
+    Producer producer;
+
+    Promise<Result, Producer> producerPromise;
+    client.createProducerAsync(topicName, 
WaitForCallbackValue<Producer>(producerPromise));
+    Future<Result, Producer> producerFuture = producerPromise.getFuture();
+    Result result = producerFuture.get(producer);
+    ASSERT_EQ(ResultOk, result);
+
+    Consumer consumer;
+    ConsumerConfiguration consConfig;
+    consConfig.setReceiverQueueSize(1);
+    Promise<Result, Consumer> consumerPromise;
+    client.subscribeAsync(topicName, subName, consConfig, 
WaitForCallbackValue<Consumer>(consumerPromise));
+    Future<Result, Consumer> consumerFuture = consumerPromise.getFuture();
+    result = consumerFuture.get(consumer);
+    ASSERT_EQ(ResultOk, result);
+    std::string temp = producer.getTopic();
+    ASSERT_EQ(temp, topicName);
+    temp = consumer.getTopic();
+    ASSERT_EQ(temp, topicName);
+    ASSERT_EQ(consumer.getSubscriptionName(), subName);
+
+    // Send 1000 messages synchronously
+    std::string msgContent = "msg-content";
+    LOG_INFO("Publishing 100 messages synchronously");
+    int msgNum = 0;
+    for (; msgNum < 100; msgNum++) {
+        std::stringstream stream;
+        stream << msgContent << msgNum;
+        Message msg = MessageBuilder().setContent(stream.str()).build();
+        ASSERT_EQ(ResultOk, producer.send(msg));
+    }
+
+    LOG_INFO("Trying to receive 100 messages");
+    Message msgReceived;
+    for (msgNum = 0; msgNum < 100; msgNum++) {
+        consumer.receive(msgReceived, 100);
+        LOG_DEBUG("Received message :" << msgReceived.getMessageId());
+        std::stringstream expected;
+        expected << msgContent << msgNum;
+        ASSERT_EQ(expected.str(), msgReceived.getDataAsString());
+        ASSERT_EQ(ResultOk, consumer.acknowledge(msgReceived));
+    }
+
+    // seek to earliest, expected receive first message.
+    result = consumer.seek(MessageId::earliest());
+    ASSERT_EQ(ResultOk, result);
+    consumer.receive(msgReceived, 100);
+    LOG_ERROR("Received message :" << msgReceived.getMessageId());
+    std::stringstream expected;
+    msgNum = 0;
+    expected << msgContent << msgNum;
+    ASSERT_EQ(expected.str(), msgReceived.getDataAsString());
+
+    ASSERT_EQ(ResultOk, consumer.unsubscribe());
+    ASSERT_EQ(ResultAlreadyClosed, consumer.close());
+    ASSERT_EQ(ResultOk, producer.close());
+    ASSERT_EQ(ResultOk, client.close());
+}

-- 
To stop receiving notification emails like this one, please contact
[email protected].

Reply via email to