github-actions[bot] commented on code in PR #67813:
URL: https://github.com/apache/doris/pull/67813#discussion_r3985802176
##########
fe/fe-core/src/main/java/org/apache/doris/cloud/transaction/CloudGlobalTransactionMgr.java:
##########
@@ -491,6 +491,9 @@ private boolean checkTransactionStateBeforeCommit(long
dbId, long transactionId)
+ "] is already aborted. abort reason: " +
transactionState.getReason());
} else if (transactionState.getTransactionStatus() ==
TransactionStatus.COMMITTED
|| transactionState.getTransactionStatus() ==
TransactionStatus.VISIBLE) {
+ if (transactionState.getTransactionStatus() ==
TransactionStatus.VISIBLE) {
Review Comment:
[P1] Do not complete a lazy retry while it is only COMMITTED
A timed-out lazy commit can durably stop at `COMMITTED` while
`TxnLazyCommitTask` is still responsible for publishing the partition versions.
This branch now treats that state as completed, but refreshes only `VISIBLE`;
its callers return success before `executeCommitTxnRequest`,
`afterCommitTxnResp`, or success callbacks run. The lazy task later changes the
transaction to `VISIBLE` without notifying FE, and the periodic syncer is not a
safe fallback because it can be disabled or race the pending partitions after
the phase-one table-version bump. Please wait/repoll through visibility and
then perform the normal cache/callback completion, or install a durable repair
owner before returning success.
##########
fe/fe-core/src/main/java/org/apache/doris/cloud/transaction/CloudGlobalTransactionMgr.java:
##########
@@ -671,6 +680,39 @@ private Map<Long, List<Long>>
updateVersion(CommitTxnResponse commitTxnResponse)
return tablePartitionMap;
}
+ private void refreshPartitionVersions(long dbId, long txnId, List<Long>
tableIds) throws UserException {
+ Database db = Env.getCurrentInternalCatalog().getDbNullable(dbId);
+ if (db == null) {
+ // The database may have been dropped after the original commit.
+ return;
+ }
+ List<CloudPartition> partitions = new ArrayList<>();
+ for (long tableId : Sets.newHashSet(tableIds)) {
+ Table table = db.getTableNullable(tableId);
+ if (table == null || !table.isManagedTable()) {
+ continue;
+ }
+ table.readLock();
+ try {
+ for (Partition partition : ((OlapTable)
table).getAllPartitions()) {
+ partitions.add((CloudPartition) partition);
+ }
+ } finally {
+ table.readUnlock();
+ }
+ }
+ try {
+ for (List<CloudPartition> batch : Lists.partition(partitions,
Config.cloud_get_version_task_batch_size)) {
Review Comment:
[P1] Install the refreshed snapshot under table version locks
`getSnapshotVisibleVersionFromMs` writes these `CloudPartition` caches one
by one, but this new path does not take the affected tables'
`versionWriteLock`s. Cached planners take `versionReadLock`s specifically while
snapshotting partition versions, and both the normal commit path and peer-FE
sync path use the matching write locks. A query can therefore interleave after
P1 is advanced but before P2 and plan one atomically visible transaction as
`[new(P1), old(P2)]`. Please fetch the remote values outside locks, then
install all affected partition values under table version-write locks acquired
in ID order.
##########
fe/fe-core/src/main/java/org/apache/doris/cloud/transaction/CloudGlobalTransactionMgr.java:
##########
@@ -671,6 +680,39 @@ private Map<Long, List<Long>>
updateVersion(CommitTxnResponse commitTxnResponse)
return tablePartitionMap;
}
+ private void refreshPartitionVersions(long dbId, long txnId, List<Long>
tableIds) throws UserException {
+ Database db = Env.getCurrentInternalCatalog().getDbNullable(dbId);
+ if (db == null) {
+ // The database may have been dropped after the original commit.
+ return;
+ }
+ List<CloudPartition> partitions = new ArrayList<>();
+ for (long tableId : Sets.newHashSet(tableIds)) {
+ Table table = db.getTableNullable(tableId);
+ if (table == null || !table.isManagedTable()) {
+ continue;
+ }
+ table.readLock();
+ try {
+ for (Partition partition : ((OlapTable)
table).getAllPartitions()) {
+ partitions.add((CloudPartition) partition);
+ }
+ } finally {
+ table.readUnlock();
+ }
+ }
+ try {
+ for (List<CloudPartition> batch : Lists.partition(partitions,
Config.cloud_get_version_task_batch_size)) {
+ CloudPartition.getSnapshotVisibleVersionFromMs(batch, false);
Review Comment:
[P1] Refresh the table cache and peer FEs too
This fallback only mutates the master FE's `CloudPartition` objects. Unlike
the normal path above, it neither advances each `OlapTable.cachedTableVersion`
nor calls `CloudFEVersionSynchronizer.pushVersionAsync`. With the default
`Long.MAX_VALUE` table/partition cache TTLs, a warmed SQL cache can still
validate against the old table version on the master, and follower/observer FEs
can keep planning with their old partition versions until the periodic daemon
runs. Thus an acknowledged visible retry can still serve stale results. Please
recover the table versions and propagate the recovered partition/table state
through the same synchronizer path as a normal commit response.
##########
fe/fe-core/src/main/java/org/apache/doris/cloud/transaction/CloudGlobalTransactionMgr.java:
##########
@@ -815,8 +857,15 @@ private void executeCommitTxnRequest(CommitTxnRequest
commitTxnRequest, long tra
StopWatch stopWatch = new StopWatch();
stopWatch.start();
try {
- txnState = commitTxn(commitTxnRequest, transactionId, is2PC,
tabletCommitInfos, tabletIds);
+ CommitTxnResponse response = commitTxn(commitTxnRequest,
transactionId, is2PC);
Review Comment:
[P1] Recover already-visible final 2PC retries
For a final 2PC retry, Meta Service returns `TXN_ALREADY_VISIBLE` plus only
`txn_info`, but `commitTxn(..., is2PC=true)` rejects that status before it can
return this response to `afterCommitTxnResp`. The apparent MoW precheck does
not cover this: `commitTransaction2PC` calls `getMowTableList(tableList,
null)`, which always returns an empty list. Thus losing the first final-commit
response leaves every retry failing with stale caches. Please treat the
already-visible final-2PC response as the durable idempotent outcome and run
the same cache recovery before completing it.
##########
fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java:
##########
@@ -5684,6 +5684,9 @@ public TStatus
reportCommitTxnResult(TReportCommitTxnResultRequest request) thro
// compaction notify update tablet stats
CloudTabletStatMgr.getInstance().addActiveTablets(tabletIds);
}
+ } catch (UserException e) {
+ LOG.warn("failed to refresh versions for reported commit, txnId:
{}", request.getTxnId(), e);
+ return new
TStatus(TStatusCode.INTERNAL_ERROR).setErrorMsgs(Collections.singletonList(e.getMessage()));
Review Comment:
[P1] Give this failed refresh a retry owner
The only production caller of this report is `send_stats_to_fe_async` in
`be/src/cloud/cloud_meta_mgr.cpp`; it converts this `INTERNAL_ERROR` to a
failed status, logs it, and then deliberately returns `Status::OK()` without
retrying. If an already-visible response reaches FE while `VersionHelper`
exhausts its bounded retries, BE has already acknowledged the commit and this
cache repair is never replayed, so the old version can remain cached after Meta
Service recovers. Please retry/schedule the repair in FE or make the BE report
path retry non-OK results; returning an error that its sole caller drops does
not close the stale-read path.
##########
fe/fe-core/src/main/java/org/apache/doris/cloud/transaction/CloudGlobalTransactionMgr.java:
##########
@@ -815,8 +857,15 @@ private void executeCommitTxnRequest(CommitTxnRequest
commitTxnRequest, long tra
StopWatch stopWatch = new StopWatch();
stopWatch.start();
try {
- txnState = commitTxn(commitTxnRequest, transactionId, is2PC,
tabletCommitInfos, tabletIds);
+ CommitTxnResponse response = commitTxn(commitTxnRequest,
transactionId, is2PC);
+ txnState = TxnUtil.transactionStateFromPb(response.getTxnInfo());
+ // A cache refresh failure must not make callbacks treat a durable
commit as failed.
txnOperated = true;
+ if (MetricRepo.isInit) {
+ MetricRepo.COUNTER_TXN_SUCCESS.increase(1L);
+
MetricRepo.HISTO_TXN_EXEC_LATENCY.update(txnState.getCommitTime() -
txnState.getPrepareTime());
+ }
+ afterCommitTxnResp(response, tabletCommitInfos, tabletIds);
Review Comment:
[P1] Do not return a normal commit failure after success callbacks
At this point Meta Service has returned the durable visible transaction and
`txnOperated` is already true, so `finally` runs
`afterCommitted`/`afterVisible`; nevertheless a refresh exception escapes from
this call. `TransactionEntry.commitTransaction` interprets that as a failed
commit, attempts to abort the already-visible transaction, and reports failure.
Routine Load is worse: `afterVisible` renews/removes the old txn task, so
retrying the returned error fails in `beforeCommitted` before cache repair can
run. Please make repair independent/retryable (or invalidate caches) while
returning an outcome that callers cannot route to abort or replay callbacks.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]