github-actions[bot] commented on code in PR #65476:
URL: https://github.com/apache/doris/pull/65476#discussion_r3567664093
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertIntoTableCommand.java:
##########
@@ -256,65 +279,62 @@ public AbstractInsertExecutor initPlan(ConnectContext
ctx, StmtExecutor stmtExec
boolean needBeginTransaction)
throws Exception {
List<String> qualifiedTargetTableName =
InsertUtils.getTargetTableQualified(originLogicalQuery, ctx);
- AbstractInsertExecutor insertExecutor;
+ AbstractInsertExecutor insertExecutor = null;
int retryTimes = 0;
ctx.getStatementContext().setIsInsert(true);
while (++retryTimes <
Math.max(ctx.getSessionVariable().dmlPlanRetryTimes, 3)) {
- TableIf targetTableIf = getTargetTableIf(ctx,
qualifiedTargetTableName);
+ boolean groupCommitBeforeAttempt = ctx.isGroupCommit();
+ Backend groupCommitBackendBeforeAttempt =
+ ctx.getStatementContext().getGroupCommitMergeBackend();
+ insertExecutor = null;
+ InsertTarget target = getTarget(ctx, qualifiedTargetTableName);
+ DatabaseIf<?> targetDatabase = target.getDatabase();
+ TableIf targetTableIf = target.getTable();
// check auth
if (needAuthCheck(targetTableIf) &&
!Env.getCurrentEnv().getAccessManager()
- .checkTblPriv(ConnectContext.get(),
targetTableIf.getDatabase().getCatalog().getName(),
- targetTableIf.getDatabase().getFullName(),
targetTableIf.getName(),
+ .checkTblPriv(ConnectContext.get(),
targetDatabase.getCatalog().getName(),
+ targetDatabase.getFullName(),
targetTableIf.getName(),
PrivPredicate.LOAD)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_TABLEACCESS_DENIED_ERROR,
"LOAD",
ConnectContext.get().getQualifiedUser(),
ConnectContext.get().getRemoteIP(),
- targetTableIf.getDatabase().getFullName()
+ targetDatabase.getFullName()
+ "." +
Util.getTempTableDisplayName(targetTableIf.getName()));
}
- BuildInsertExecutorResult buildResult;
+ ExecutorFactory executorFactory;
try {
// use originLogicalQuery to build logicalQuery again.
- buildResult = initPlanOnce(ctx, stmtExecutor, targetTableIf);
+ executorFactory = initPlanOnce(ctx, stmtExecutor,
targetTableIf);
} catch (Throwable e) {
Throwables.throwIfInstanceOf(e, RuntimeException.class);
throw new IllegalStateException(e.getMessage(), e);
}
- insertExecutor = buildResult.executor;
- parsedPlan =
Optional.ofNullable(buildResult.planner.getParsedPlan());
- Plan analyzedPlan = buildResult.planner.getAnalyzedPlan();
+ parsedPlan =
Optional.ofNullable(executorFactory.planner.getParsedPlan());
+ Plan analyzedPlan = executorFactory.planner.getAnalyzedPlan();
lineagePlan = Optional.ofNullable(analyzedPlan);
- if (!needBeginTransaction) {
- return insertExecutor;
- }
- List<TableStreamUpdateInfo> infos =
StreamConsumptionInfoExtractor.extract(analyzedPlan);
+ List<TableStreamUpdateInfo> infos = needBeginTransaction
+ ? StreamConsumptionInfoExtractor.extract(analyzedPlan) :
Lists.newArrayList();
if (!infos.isEmpty()) {
if (!Config.enable_feature_binlog) {
throw new AnalysisException("Insert plan with Table stream
failed."
+ " should enable binlog feature in FE config.");
}
- // put offset into executor
- insertExecutor.setStreamUpdateInfos(infos);
- insertExecutor.registerListener(new InsertExecutorListener() {
- @Override
- public void beforeComplete(AbstractInsertExecutor
insertExecutor, StmtExecutor executor,
- long jobId) throws Exception {
- TransactionState transactionState =
Env.getCurrentGlobalTransactionMgr()
-
.getTransactionState(insertExecutor.getDatabase().getId(),
- insertExecutor.getTxnId());
-
transactionState.setStreamUpdateInfos(insertExecutor.getStreamUpdateInfos());
- }
- });
}
// lock after plan and check does table's schema changed to ensure
we lock table order by id.
- TableIf newestTargetTableIf = getTargetTableIf(ctx,
qualifiedTargetTableName);
+ InsertTarget newestTarget = getTarget(ctx,
qualifiedTargetTableName);
+ DatabaseIf<?> newestTargetDatabase = newestTarget.getDatabase();
+ TableIf newestTargetTableIf = newestTarget.getTable();
newestTargetTableIf.readLock();
try {
- if (targetTableIf.getId() != newestTargetTableIf.getId()) {
- LOG.warn("insert plan failed {} times. query id is {}.
table id changed from {} to {}",
+ if (targetDatabase.getId() != newestTargetDatabase.getId()
+ || targetTableIf.getId() !=
newestTargetTableIf.getId()) {
+ LOG.warn("insert plan failed {} times. query id is {}.
target id changed from {}.{} to {}.{}",
retryTimes, DebugUtil.printId(ctx.queryId()),
- targetTableIf.getId(),
newestTargetTableIf.getId());
+ targetDatabase.getId(), targetTableIf.getId(),
+ newestTargetDatabase.getId(),
newestTargetTableIf.getId());
+ ctx.setGroupCommit(groupCommitBeforeAttempt);
Review Comment:
This retry path still preserves another failed-attempt `StatementContext`
field. The first planning attempt runs `CheckPrivileges` and sets
`stmtCtx.privChecked=true`; if this branch discards that attempt because the
target id/schema changed, the code restores only group-commit state before
`continue`. The accepted retry then reuses the same `StatementContext`, so
`CheckPrivileges.rewriteRoot()` returns before visiting source relations,
views, CTE producers, or TVFs, leaving only the explicit target LOAD check.
Please reset per-attempt privilege state here, or use a fresh per-attempt
`StatementContext` that preserves only intentional cross-attempt fields.
##########
fe/fe-core/src/main/java/org/apache/doris/dictionary/DictionaryManager.java:
##########
@@ -495,16 +521,28 @@ public void dataLoad(ConnectContext ctx, Dictionary
dictionary, boolean adaptive
}
}
+ while
(DebugPointUtil.isEnable("DictionaryManager.dataLoad.blockBeforeCommit")) {
+ Thread.sleep(10);
+ }
+
// commit and check the result. not modify metadata so dont need lock.
+ long stagedVersion = dictionary.getVersion();
if (!commitNowVersion(ctx, dictionary)) {
Review Comment:
This rollback still assumes commit failure means no BE made the staged
version visible, but `commitNowVersion()` treats mixed results as a plain
false. If one BE returns OK and a later BE fails or times out, the OK BE has
already moved `stagedVersion` into `_dict_id_to_dict_map` and erased
`_refreshing_dict_map`; the subsequent `abortSpecificVersion(...,
stagedVersion)` cannot undo it because BE abort only removes refreshing
entries. FE then decrements back to the old version and future `dict_get` plans
carry that old version, while the committed BE requires the staged version
exactly. Please handle mixed commit results explicitly, for example by not
rolling FE metadata back once any BE has committed, or by adding a BE protocol
that can restore the previous committed dictionary.
##########
regression-test/suites/dictionary_p0/test_create_drop_sync.groovy:
##########
@@ -105,4 +132,40 @@ suite('test_create_drop_sync') {
LAYOUT(HASH_MAP)
properties('data_lifetime'='600');
"""
-}
\ No newline at end of file
+
+ waitAllDictionariesReady()
+ long originalVersion = (sql "SHOW DICTIONARIES")[0][3].toLong()
+ def commitFailureFuture
+ try {
+
GetDebugPoint().enableDebugPointForAllFEs('DictionaryManager.dataLoad.blockBeforeCommit')
Review Comment:
This cleanup is split across two blocks, so
`DictionaryManager.commitNowVersion.forceFailure` can leak if the setup fails
before `commitFailureFuture.get()` is reached. For example, `awaitUntil(10)`
throws on timeout and the first `finally` disables only `blockBeforeCommit`,
leaving the force-failure debug point active on every FE. Since that debug
point makes every later dictionary commit return false, please disable both
debug points in the same outer `finally` that covers their
`enableDebugPointForAllFEs` calls.
##########
fe/fe-core/src/main/java/org/apache/doris/dictionary/DictionaryManager.java:
##########
@@ -433,7 +459,8 @@ public void dataLoad(ConnectContext ctx, Dictionary
dictionary, boolean adaptive
baseCommand.setJobId(DICTIONARY_JOB_ID);
}
- InsertIntoDictionaryCommand command = new
InsertIntoDictionaryCommand(baseCommand, dictionary, adaptiveLoad);
+ InsertIntoDictionaryCommand command = new InsertIntoDictionaryCommand(
Review Comment:
The pre-commit failure paths still do not clean up staged dictionary data on
BEs. A dictionary sink stages the refresh at EOS via
`DictionaryFactory::refresh_dict()`, but if one BE has staged data and
`command.run()` later throws, or leaves an error in `ctx.getState()`, the
following catch/error branches only restore FE status and rethrow. They never
call `abortSpecificVersion()`, and the later dropped-dictionary/commit-failure
abort blocks are not reached. Because BE status collection and `delete_dict()`
only look at committed dictionaries, the `_refreshing_dict_map` entry can
remain invisible until another refresh overwrites it. Please abort the staged
version in both pre-commit error paths when the selected BEs are known, using
the current version for partial loads and `version + 1` for full loads.
--
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]