-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
[Transaction] Fix transaction sequenceId generate error. #13209
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,46 +18,87 @@ | |
*/ | ||
package org.apache.pulsar.transaction.coordinator.impl; | ||
|
||
import io.netty.buffer.ByteBuf; | ||
import lombok.Getter; | ||
import org.apache.bookkeeper.client.LedgerHandle; | ||
import org.apache.bookkeeper.client.api.LedgerEntry; | ||
import org.apache.bookkeeper.mledger.impl.OpAddEntry; | ||
import org.apache.bookkeeper.mledger.intercept.ManagedLedgerInterceptor; | ||
import org.apache.pulsar.transaction.coordinator.proto.TransactionMetadataEntry; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.util.Map; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.concurrent.atomic.AtomicLong; | ||
|
||
/** | ||
* Store max sequenceID in ManagedLedger properties, in order to recover transaction log. | ||
*/ | ||
public class MLTransactionLogInterceptor implements ManagedLedgerInterceptor { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(MLTransactionLogInterceptor.class); | ||
private static final long TC_ID_NOT_USED = -1L; | ||
public static final String MAX_LOCAL_TXN_ID = "max_local_txn_id"; | ||
|
||
private volatile long maxLocalTxnId = -1; | ||
@Getter | ||
private final AtomicLong sequenceId = new AtomicLong(TC_ID_NOT_USED); | ||
|
||
@Override | ||
public OpAddEntry beforeAddEntry(OpAddEntry op, int numberOfMessages) { | ||
return null; | ||
return op; | ||
} | ||
|
||
// When all of ledger have been deleted, we will generate sequenceId from managedLedger properties | ||
@Override | ||
public void onManagedLedgerPropertiesInitialize(Map<String, String> propertiesMap) { | ||
if (propertiesMap == null || propertiesMap.size() == 0) { | ||
return; | ||
} | ||
|
||
if (propertiesMap.containsKey(MAX_LOCAL_TXN_ID)) { | ||
sequenceId.set(Long.parseLong(propertiesMap.get(MAX_LOCAL_TXN_ID))); | ||
} | ||
} | ||
|
||
// When we don't roll over ledger, we can init sequenceId from the getLastAddConfirmed transaction metadata entry | ||
@Override | ||
public CompletableFuture<Void> onManagedLedgerLastLedgerInitialize(String name, LedgerHandle ledgerHandle) { | ||
return CompletableFuture.completedFuture(null); | ||
public CompletableFuture<Void> onManagedLedgerLastLedgerInitialize(String name, LedgerHandle lh) { | ||
CompletableFuture<Void> promise = new CompletableFuture<>(); | ||
if (lh.getLastAddConfirmed() >= 0) { | ||
lh.readAsync(lh.getLastAddConfirmed(), lh.getLastAddConfirmed()).whenComplete((entries, ex) -> { | ||
if (ex != null) { | ||
log.error("[{}] Read last entry error.", name, ex); | ||
promise.completeExceptionally(ex); | ||
} else { | ||
if (entries != null) { | ||
try { | ||
LedgerEntry ledgerEntry = entries.getEntry(lh.getLastAddConfirmed()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
if (ledgerEntry != null) { | ||
TransactionMetadataEntry lastConfirmEntry = new TransactionMetadataEntry(); | ||
ByteBuf buffer = ledgerEntry.getEntryBuffer(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The buffer should be released after getting the max local txn ID. |
||
lastConfirmEntry.parseFrom(buffer, buffer.readableBytes()); | ||
this.sequenceId.set(lastConfirmEntry.getMaxLocalTxnId()); | ||
} | ||
entries.close(); | ||
promise.complete(null); | ||
} catch (Exception e) { | ||
log.error("[{}] Failed to recover the tc sequenceId from the last add confirmed entry.", | ||
name, e); | ||
promise.completeExceptionally(e); | ||
} | ||
} else { | ||
promise.complete(null); | ||
} | ||
} | ||
}); | ||
} else { | ||
promise.complete(null); | ||
} | ||
return promise; | ||
} | ||
|
||
// roll over ledger will update sequenceId to managedLedger properties | ||
@Override | ||
public void onUpdateManagedLedgerInfo(Map<String, String> propertiesMap) { | ||
propertiesMap.put(MAX_LOCAL_TXN_ID, maxLocalTxnId + ""); | ||
} | ||
|
||
protected void setMaxLocalTxnId(long maxLocalTxnId) { | ||
this.maxLocalTxnId = maxLocalTxnId; | ||
propertiesMap.put(MAX_LOCAL_TXN_ID, sequenceId.get() + ""); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,10 +42,14 @@ public CompletableFuture<TransactionMetadataStore> openStore(TransactionCoordina | |
ManagedLedgerConfig managedLedgerConfig, | ||
TransactionTimeoutTracker timeoutTracker, | ||
TransactionRecoverTracker recoverTracker) { | ||
MLTransactionLogInterceptor mlTransactionLogInterceptor = new MLTransactionLogInterceptor(); | ||
managedLedgerConfig.setManagedLedgerInterceptor(new MLTransactionLogInterceptor()); | ||
MLTransactionLogImpl txnLog = new MLTransactionLogImpl(transactionCoordinatorId, | ||
managedLedgerFactory, managedLedgerConfig); | ||
|
||
// MLTransactionLogInterceptor will init sequenceId and update the sequenceId to managedLedger properties. | ||
return txnLog.initialize().thenApply(__ -> | ||
new MLTransactionMetadataStore(transactionCoordinatorId, txnLog, timeoutTracker, recoverTracker)); | ||
new MLTransactionMetadataStore(transactionCoordinatorId, txnLog, timeoutTracker, | ||
recoverTracker, mlTransactionLogInterceptor.getSequenceId())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's better to introduce modify sequence ID method for |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'd better return long not AtomicLong for the getter method.