Skip to content

[Transaction] Transaction pending ack lazy init.#11091

Merged
congbobo184 merged 18 commits intoapache:masterfrom
congbobo184:congbobo184_pending_ack_lazy_load
Oct 9, 2021
Merged

[Transaction] Transaction pending ack lazy init.#11091
congbobo184 merged 18 commits intoapache:masterfrom
congbobo184:congbobo184_pending_ack_lazy_load

Conversation

@congbobo184
Copy link
Contributor

Motivation

now, in broker.conf transactionCoordinatorEnabled=true MLPendingAck will init manageLedger, some ack will not use transaction, so don't need to init manageLedger. When this sub use transaction, we can lazy init PendingAckHandle.

implement

When this sub use transaction, we can lazy init PendingAckHandle.

Verifying this change

Add the tests for it

Does this pull request potentially affect one of the following parts:
If yes was chosen, please highlight the changes

Dependencies (does it add or upgrade a dependency): (no)
The public API: (no)
The schema: (no)
The default values of configurations: (no)
The wire protocol: (no)
The rest endpoints: (no)
The admin cli options: (no)
Anything that affects deployment: (no)

Copy link
Contributor

@eolivelli eolivelli left a comment

Choose a reason for hiding this comment

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

very interesting work !
it is a big patch. I will do a second pass tomorrow

@hangc0276
Copy link
Contributor

@merlimat @codelipenghui @eolivelli Would you please help review this PR ?

Copy link
Contributor

@eolivelli eolivelli left a comment

Choose a reason for hiding this comment

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

Lgtm

@hangc0276
Copy link
Contributor

move to 2.8.2.

…_ack_lazy_load

# Conflicts:
#	managed-ledger/src/main/java/org/apache/bookkeeper/mledger/ManagedLedgerFactory.java
#	managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedLedgerFactoryImpl.java
#	managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/MetaStore.java
#	managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/MetaStoreImpl.java
#	pulsar-broker/src/test/java/org/apache/pulsar/broker/transaction/buffer/TransactionBufferClientTest.java
initPendingAckStore();
return completableFuture;
} else if (checkIfReady()) {

Copy link
Contributor

Choose a reason for hiding this comment

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

Miss something here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When ready, we should let the code continue to execute downward

synchronized (PendingAckHandleImpl.this) {
if (!acceptQueue.isEmpty()) {
CompletableFuture<Void> completableFuture = new CompletableFuture<>();
addIndividualAcknowledgeMessageRequest(txnID, positions, completableFuture);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why we can't add to the queue if the queue is empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We need to make sure that Ack is in order

}
}

if (!acceptQueue.isEmpty() && !isInCacheRequest) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it able to refine the code here? looks like the above part has check isInCacheRequest but here check it again.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

optimized, please review again


if (pendingAckHandle.changeToReadyState()) {
pendingAckHandle.completeHandleFuture();
pendingAckHandle.handleCacheRequest();
Copy link
Contributor

Choose a reason for hiding this comment

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

We will hold the lock for a long time here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In order to ensure the order of ack, I think we should continue to acquire this lock

Copy link
Contributor

@gaoran10 gaoran10 left a comment

Choose a reason for hiding this comment

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

Great work! Left some comments.

It seems that the transaction pending ack lay initialization increases the logic complexity, does this is necessary?

if (pendingAckHandle.changeToReadyState()) {
pendingAckHandle.completeHandleFuture();
pendingAckHandle.handleCacheRequest();
log.info("Topic name : [{}], SubName : [{}] pending ack state reply success!",
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems that this log is the same as the above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch

log.info("Topic name : [{}], SubName : [{}] pending ack state reply success!",
pendingAckHandle.getTopicName(), pendingAckHandle.getSubName());
} else {
log.error("Topic name : [{}], SubName : [{}] pending ack state reply fail!",
Copy link
Contributor

Choose a reason for hiding this comment

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

If the pending ack handle relay failed, how to reply again?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

if change ready state fail, represent that the pending ack has already close


private void initPendingAckStore() {
if (changeToInitializingState()) {
synchronized (PendingAckHandleImpl.this) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure why need this lock, it seems that only one thread will perform the replay operation because only one thread could change the state successfully.

Copy link
Contributor Author

@congbobo184 congbobo184 Sep 3, 2021

Choose a reason for hiding this comment

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

if change Initializing state success, in this time, we can change the state to closed, when change state to closed, we don't need to do replay op.

@Override
public synchronized CompletableFuture<Void> commitTxn(TxnID txnID, Map<String, Long> properties,
long lowWaterMark) {
long lowWaterMark, boolean isInCacheRequest) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems that the param isInCacheRequest isn't used, do we need to add this param?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch, we should add the commit request !isInCacheRequest in to a queue.

@Override
public synchronized CompletableFuture<Void> abortTxn(TxnID txnId, Consumer consumer, long lowWaterMark) {
public synchronized CompletableFuture<Void> abortTxn(TxnID txnId, Consumer consumer,
long lowWaterMark, boolean isInCacheRequest) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems that the param isInCacheRequest isn't used, do we need to add this param?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch, we should add the abort request !isInCacheRequest in to a queue.

public CompletableFuture<Void> close() {
return this.pendingAckStoreFuture.thenAccept(PendingAckStore::closeAsync);
changeToCloseState();
synchronized (PendingAckHandleImpl.this) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need this lock?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

same with init


} else {
return FutureUtil.failedFuture(
new ServiceUnitNotReadyException("PendingAckHandle not replay complete!"));
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this error log is right? It seems that the state is Error or Close.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch

if (!isInCacheRequest) {
if (!checkIfReady()) {
synchronized (PendingAckHandleImpl.this) {
if (state == State.Initializing) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems that this check is repeated many times, maybe we could add a method to make this check.

such as

public boolean needCacheRequest() {
  if (state == State.Initializing ||) {
    return true;
  } else if (state == State.None) {
    initPendingAckStore();
    return true;
  } else if (checkIfReady()) {
    // do nothing
  } else {
    return FutureUtil.failedFuture(
           new ServiceUnitNotReadyException("PendingAckHandle replay failed!"));
  }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

in different state, commit, ack and abort will do different op, so this method return what?

Copy link
Contributor

Choose a reason for hiding this comment

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

If the return value is true, we could cache the request in the queue.

boolean isInCacheRequest) {
if (!isInCacheRequest) {
if (!checkIfReady()) {
synchronized (PendingAckHandleImpl.this) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this lock is used to make sure the state doesn't be changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes

@congbobo184
Copy link
Contributor Author

@gaoran10 thanks for you review, fix some comment, please review again.

@eolivelli eolivelli modified the milestones: 2.9.0, 2.10.0 Oct 6, 2021
@congbobo184 congbobo184 merged commit 8b50af5 into apache:master Oct 9, 2021
@codelipenghui codelipenghui modified the milestones: 2.10.0, 2.9.0 Nov 29, 2021
bharanic-dev pushed a commit to bharanic-dev/pulsar that referenced this pull request Mar 18, 2022
## Motivation
now, in `broker.conf` `transactionCoordinatorEnabled=true` MLPendingAck will init manageLedger, some ack will not use transaction, so don't need to init manageLedger. When this sub use transaction, we can lazy init `PendingAckHandle`.

## implement
When this sub use transaction, we can lazy init `PendingAckHandle`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants