Skip to content

CPP add receiveAsync API#577

Closed
rdhabalia wants to merge 1 commit intoapache:masterfrom
rdhabalia:async_receive
Closed

CPP add receiveAsync API#577
rdhabalia wants to merge 1 commit intoapache:masterfrom
rdhabalia:async_receive

Conversation

@rdhabalia
Copy link
Contributor

Motivation

Sometimes, client wants to receive a message async to complete specific depending task or request. eg. java-client library has receiveAsync api which is used by proxy to receive msg async and completes pending proxy request on message-received. CPP-client library doesn't have receiveAsync api.

Modifications

Add receiveAsync api to cpp-client library.

@rdhabalia rdhabalia added component/c++ type/feature The PR added a new feature or issue requested a new feature labels Jul 17, 2017
@rdhabalia rdhabalia added this to the 1.19.0-incubating milestone Jul 17, 2017
@rdhabalia rdhabalia self-assigned this Jul 17, 2017
@rdhabalia rdhabalia requested review from jai1, merlimat and saandrews July 17, 2017 23:18
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove this mutex ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you see any use of this mutex_ here?
in fact I think it can create a dead-lock.
eg: if incomingMessages-queue is empty then receive() will take the mutex and wait on it until message is received. Now, when message is received and if it tries to take mutex_ then it will create a deadlock.
However, for batch msg we are using pendingReceiveMutex_ into receiveIndividualMessagesFromBatch to sync msg-queue and pending-receive queue.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A. Yes I need the mutex since batchAcknowledgementTracker_ is not thread safe
B. As discussed - I don't see a deadlock

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Receive Callback should return a Result along with Message

Copy link
Contributor Author

@rdhabalia rdhabalia Jul 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a async-receive and it will never have any failure as a result and it only returns a message when available so, I think there is no need of result in this case.??

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if consumer is not initialized - most of the other async operations in Consumer.cc return a ResultConsumerNotInitialized .

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then we should add request into pending-queue and it should complete the callback when the message will be available.

Copy link
Contributor Author

@rdhabalia rdhabalia Jul 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

however, we should immediately fail the callback if consumer is closing or already closed (I will make similar change in java-client as well). so, I have added the result into the callback.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UnboundedBlockingQueue is not a good idea, since you don't want to block if the queue is empty
I would suggest using a vector or a list.

Copy link
Contributor Author

@rdhabalia rdhabalia Jul 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. actually, my motivation to use UnboundedBlockingQueue to have thread-safe, unbounded collection. Do you see any specific issue with UnboundedBlockingQueue? Also I think std::list/queue creates a copy of object on pop_front()/front()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replaced UnboundedBlockingQueue with std::queue

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for a separate pendingReceiveMutex_ - I suggest to use mutex_

Copy link
Contributor Author

@rdhabalia rdhabalia Jul 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pendingReceiveMutex_ is a mutex which requires to sync pendingReceives-queue and incomingMessages-queue together to make sure message-queue is empty while adding into pending-queue and also requires to prevent race-condition on pendingReceive_ .
mutex_ will not work here for example:

  • receive() is a blocking call which takes locks on mutext_ until message is received
  • now, if we use the same mutex_ on message-received then it will create a deadlock.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, but the problem is that in PartitionedConsumerImpl:receive we should have released the lock after we checked the state_ and messageListener_ condition since the other data structures are thread safe (see ConsumerImpl::receiveHelper to understand my point)

If you fix this locking then you won't require pendingReceiveMutex_

I am stressing about this minor thing because in all classes we have only one lock to protect all members (mutex_), so as far as possible I don't want to break this convention.

@rdhabalia rdhabalia force-pushed the async_receive branch 3 times, most recently from 64f10fc to 36795a0 Compare July 18, 2017 23:32
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, but the problem is that in PartitionedConsumerImpl:receive we should have released the lock after we checked the state_ and messageListener_ condition since the other data structures are thread safe (see ConsumerImpl::receiveHelper to understand my point)

If you fix this locking then you won't require pendingReceiveMutex_

I am stressing about this minor thing because in all classes we have only one lock to protect all members (mutex_), so as far as possible I don't want to break this convention.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This unlock not required

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not :

    void PartitionedConsumerImpl::messageReceived(Consumer consumer, const Message& msg) {
        LOG_DEBUG("Received Message from one of the partition - " << msg.impl_->messageId.partition_);
        Lock lock(mutex_);
        if(!pendingReceives_.empty()) {
            ReceiveCallback callback = pendingReceives_.front();
            pendingReceives_.pop();
            lock.unlock();
            callback(ResultOk, msg);
            unAckedMessageTrackerPtr_->add(msg.getMessageId());
            return;
        }
        messages_.push(msg);
        if (messageListener_) {
            listenerExecutor_->postWork(boost::bind(&PartitionedConsumerImpl::internalListener, shared_from_this(), consumer));
        }
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for receiveAsync, we need to access pendingReceives_ and messages_ atomically. so, if we release lock before pushing to messages_ then it will face race condition while receiving msg using receiveAsync and callback will never be called.
eg:

  • thread-1 comes to receiveAsync() which first checks messages_ if it's empty then it adds callback into pendingReceives_
  • thread-2: in this method, if it adds the message into the messages_ same time then : message will sit into messages_ queue and will never be notified for receiveAsync-Callback.

Therefore, we need separate mutex to access pendingReceives_ and messages_ atomically.

Copy link
Contributor

@jai1 jai1 Jul 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for notifyPendingReceivedCallback function, just call the callback

@asfgit
Copy link

asfgit commented Jul 29, 2017

Refer to this link for build results (access rights to CI server needed):
https://builds.apache.org/job/pulsar-pull-request/23/

@sijie
Copy link
Member

sijie commented Apr 25, 2018

@rdhabalia what is the current state of this PR? are we targeting this for 2.0? If not, can we move it to 2.1?

@sijie
Copy link
Member

sijie commented Apr 25, 2018

it seems that this PR was from 1.19.0-incubating. so I will move it out of 2.0, if we need this for 2.0, let's add it back.

@rdhabalia
Copy link
Contributor Author

sure. I will rebase it and we can take it to next release.

@sijie sijie removed this from the 2.1.0-incubating milestone Jun 18, 2018
@sijie sijie added this to the 2.2.0-incubating milestone Jun 18, 2018
@sijie sijie removed this from the 2.2.0-incubating milestone Sep 12, 2018
@rdhabalia rdhabalia force-pushed the async_receive branch 4 times, most recently from fcff2bc to cb5d348 Compare January 18, 2019 01:51
replace UnboundedBlockingQueue with queue

add result to receiveAsync-callback to fail callback on consumer closing/closed

check uninitialized consumer state

notify callback directly
@rdhabalia rdhabalia closed this Jan 18, 2019
sijie pushed a commit that referenced this pull request Jan 21, 2019
### Motivation

In many cases, client requires receiveAsync() api in Consumer. This api is already available into java-client but doesn't exist into CPP-client.

### Modification

Add support for receiveAsync() api in cpp-client consumer.

This PR is rebased and reopened from #577
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type/feature The PR added a new feature or issue requested a new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants