Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid using same OpAddEntry between different ledger handles #5942

Merged
merged 6 commits into from
Jan 9, 2020

Conversation

codelipenghui
Copy link
Contributor

@codelipenghui codelipenghui commented Dec 26, 2019

Fixes #5588

Motivation

Avoid using same OpAddEntry between different ledger handles.

Modifications

Add state for OpAddEntry, if op handled by new ledger handle, the op will set to CLOSED state, after the legacy callback happens will check the op state, only INITIATED can be processed.

When ledger rollover happens, pendingAddEntries will be processed. when process pendingAddEntries, will create a new OpAddEntry by the old OpAddEntry to avoid different ledger handles use same OpAddEntry.

Verifying this change

Added new unit test

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)

Documentation

  • Does this pull request introduce a new feature? (no)

@codelipenghui codelipenghui requested review from rdhabalia, sijie and merlimat and removed request for rdhabalia and sijie December 26, 2019 09:21
@codelipenghui
Copy link
Contributor Author

run cpp tests
run integration tests

@codelipenghui
Copy link
Contributor Author

run cpp tests

2 similar comments
@codelipenghui
Copy link
Contributor Author

run cpp tests

@tuteng
Copy link
Member

tuteng commented Dec 31, 2019

run cpp tests

internalAsyncAddEntry(addOperation);
}));
}

private synchronized void internalAsyncAddEntry(OpAddEntry addOperation) {
pendingAddEntries.add(addOperation);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this real root cause of #5588 or it just a patch to avoid such behavior?

Copy link
Member

Choose a reason for hiding this comment

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

The root cause of #5588 is an entry is "re-used" between ledgers. The code at line 1297 is the fix.

internalAsyncAddEntry(addOperation);
}));
}

private synchronized void internalAsyncAddEntry(OpAddEntry addOperation) {
pendingAddEntries.add(addOperation);
Copy link
Member

Choose a reason for hiding this comment

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

The root cause of #5588 is an entry is "re-used" between ledgers. The code at line 1297 is the fix.

@@ -1294,9 +1293,24 @@ public synchronized void updateLedgersIdsComplete(Stat stat) {
log.debug("[{}] Resending {} pending messages", name, pendingAddEntries.size());
}

// Avoid use same OpAddEntry between different ledger handle
int pendingSize = pendingAddEntries.size();
Copy link
Member

Choose a reason for hiding this comment

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

hmm, this doesn't seem to be correct to me. you need to preserve the order when adding the newly created ops back to the queue.

what you need to do:

  • drain the pendingAddEntries queue;
  • create a new OpAddEntry for each entry
  • add these ops into an intermediate list in the order of how they are drained
  • after the pendingAddEntries are drained, add the intermediate list back to the pendingAddEntries queue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The pendingAddEntries is ConcurrentLinkedQueue, there is no drainTo method in ConcurrentLinkedQueue, maybe we can use pendingAddEntries.toArray() and then create a new OpAddEntry for each item of the array and add the new entry to an intermediate list.

Copy link
Member

Choose a reason for hiding this comment

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

I see. it is a queue here.

LedgerHandle ledger;
private long entryId;

@SuppressWarnings("unused")
private volatile AddEntryCallback callback;
private static final AtomicReferenceFieldUpdater<OpAddEntry, AddEntryCallback> callbackUpdater =
Copy link
Member

Choose a reason for hiding this comment

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

do we need the change 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.

I just copy the updater close to the field


if (!STATE_UPDATER.compareAndSet(OpAddEntry.this, State.INITIATED, State.COMPLETED)) {
log.warn("[{}] The add op is terminal legacy callback for entry {}-{} adding.", ml.getName(), lh.getId(), entryId);
OpAddEntry.this.recycle();
Copy link
Member

Choose a reason for hiding this comment

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

since we are creating a new entry when retrying the ops on the new ledger, do we need to introduce the state field and recycle here?

Since this op is only used by the old ledger handler, it will not be reused across ledgers. It should already be recycled correctly.

if (existsOp != null) {
// If op is used by another ledger handle, we need to close it and create a new one
if (existsOp.ledger != null) {
existsOp.close();
Copy link
Member

Choose a reason for hiding this comment

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

not sure we need to close here. I think once we duplicate the operation, we can just let the original callback close the old op. so it seems to me that we don't need introducing another state field 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.

We need to close the original op, otherwise when the old op callback, will poll the first op in the pendingAddEntries

But, the first op is the new op we replaced.

@codelipenghui codelipenghui merged commit 7ec17b2 into apache:master Jan 9, 2020
@sijie sijie added this to the 2.5.1 milestone Jan 13, 2020
@sijie sijie modified the milestones: 2.5.1, 2.6.0 Jan 22, 2020
tuteng pushed a commit to AmateurEvents/pulsar that referenced this pull request Feb 23, 2020
…5942)

### Motivation

Avoid using same OpAddEntry between different ledger handles.

### Modifications

Add state for OpAddEntry, if op handled by new ledger handle, the op will set to CLOSED state, after the legacy callback happens will check the op state, only INITIATED can be processed.

When ledger rollover happens, pendingAddEntries will be processed. when process pendingAddEntries, will create a new OpAddEntry by the old OpAddEntry to avoid different ledger handles use same OpAddEntry.
tuteng pushed a commit to AmateurEvents/pulsar that referenced this pull request Mar 21, 2020
…5942)

### Motivation

Avoid using same OpAddEntry between different ledger handles.

### Modifications

Add state for OpAddEntry, if op handled by new ledger handle, the op will set to CLOSED state, after the legacy callback happens will check the op state, only INITIATED can be processed.

When ledger rollover happens, pendingAddEntries will be processed. when process pendingAddEntries, will create a new OpAddEntry by the old OpAddEntry to avoid different ledger handles use same OpAddEntry.

(cherry picked from commit 7ec17b2)
tuteng pushed a commit that referenced this pull request Apr 13, 2020
### Motivation

Avoid using same OpAddEntry between different ledger handles.

### Modifications

Add state for OpAddEntry, if op handled by new ledger handle, the op will set to CLOSED state, after the legacy callback happens will check the op state, only INITIATED can be processed.

When ledger rollover happens, pendingAddEntries will be processed. when process pendingAddEntries, will create a new OpAddEntry by the old OpAddEntry to avoid different ledger handles use same OpAddEntry.

(cherry picked from commit 7ec17b2)
@codelipenghui codelipenghui deleted the issue-5588 branch April 24, 2020 08:23
jiazhai pushed a commit to jiazhai/pulsar that referenced this pull request May 18, 2020
…5942)

### Motivation

Avoid using same OpAddEntry between different ledger handles.

### Modifications

Add state for OpAddEntry, if op handled by new ledger handle, the op will set to CLOSED state, after the legacy callback happens will check the op state, only INITIATED can be processed.

When ledger rollover happens, pendingAddEntries will be processed. when process pendingAddEntries, will create a new OpAddEntry by the old OpAddEntry to avoid different ledger handles use same OpAddEntry.

(cherry picked from commit 7ec17b2)
huangdx0726 pushed a commit to huangdx0726/pulsar that referenced this pull request Aug 24, 2020
…5942)

### Motivation

Avoid using same OpAddEntry between different ledger handles.

### Modifications

Add state for OpAddEntry, if op handled by new ledger handle, the op will set to CLOSED state, after the legacy callback happens will check the op state, only INITIATED can be processed.

When ledger rollover happens, pendingAddEntries will be processed. when process pendingAddEntries, will create a new OpAddEntry by the old OpAddEntry to avoid different ledger handles use same OpAddEntry.
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.

java.lang.IllegalArgumentException: ledgerId doesn't match with acked ledgerId
5 participants