yujun777 commented on issue #68679:
URL: https://github.com/apache/doris/issues/68679#issuecomment-6142250468
## A systematic pass over every point between the two halves
Added after #68662 reached master. The three numbered paths in the body are
a subset of the list below; this comment enumerates all of them with the code
path for each, and separates **confirmed** (read on master) from **pending**
(needs a reproduction).
### Where the two durable facts separate
- The **rows and the table-stream offsets** become durable together at the
inner insert's commit: `InsertIntoTableCommand` hangs the offsets on the
transaction, and `DatabaseTransactionMgr.updateCatalogAfterCommitted` applies
them on commit and replays them.
- The **publication** is a second, separate catalog action:
`InsertOverwriteUtil.replacePartition` → `Env.replaceTempPartition` /
`ReplacePartitionOperationLog`, driven from `InsertOverwriteTableCommand`
(explicit partitions) or `InsertOverwriteManager.replacePartitionsOfTaskGroup`
(`PARTITION(*)`).
- In between, the system holds **temp partitions that are durable but
unpublished** — `InsertOverwriteManager` is itself persisted (image plus its
own `InsertOverwriteLog` ADD/DROP/CANCEL journal records) — and **offsets that
are already advanced**.
### Local (OLAP) target
1. **FE crash between the halves** — confirmed. Nothing publishes the
committed rows: after a restart `InsertOverwriteManager.runAfterCatalogReady`
only rolls back tasks that are *marked cancel*, so the task and its temp
partitions survive, unpublished, while the offsets stay advanced. A re-run
reads from the advanced position and that range is silently missing.
2. **Master switch** — confirmed. `Env.transferToMaster` → `allTaskFail()` →
`taskFail` → `rollback` **drops** every in-flight task's temp partitions. The
committed rows are gone entirely and the offsets stay advanced.
3. **The swap itself fails** — confirmed. `replacePartitionsOfTaskGroup`
catches, logs, and rethrows; `taskGroupFail`/`taskFail` → `rollback` drops the
temp partitions. The error does reach the client, but nothing moves the offsets
back, so a re-run still skips the range.
4. **Cancellation and the error response** — fixed by #68662: before the
insert, after an insert that committed nothing, while the swap waits for the
target's write lock, and the `insert_visible_timeout_return_mode=error`
publication timeout, plus a target dropped under the swap.
5. **Crash inside `finishTaskGroup`** — checked, and the outcome is *not*
what I expected when writing this list. The replacement has already run, so the
rows are published and correct; only the per-task bookkeeping
(`logInsertOverwrite(ADD)` then `taskSuccess`, which logs DROP) is half done,
leaving some tasks registered. Those are never cleaned (`runAfterCatalogReady`
handles only cancelled tasks), but they are also **harmless**: after the swap
the temp names no longer exist — the replacement runs with
`PROPERTIES_USE_TEMP_PARTITION_NAME=false`, so the new data takes the
*original* partition names — and `InsertOverwriteUtil.dropPartitions` looks the
name up with `getPartition(name, true)`, which reads only `tempPartitions` and
`continue`s when it is absent. So a later `taskFail`/`allTaskFail` over such a
task is a no-op that removes it, and it cannot reach the live partition. What
is left is a stale entry in `tasks`, not data loss.
One narrow residual, **pending a reproduction**: the temp names are
generated as `iot_temp_<threadId>_<partitionName>` with no statement identity
in them, and the swap frees that name again. If a crash leaves a task
registered and a later overwrite on the same table happens to run on a reused
thread id, it recreates the same temp name — and a master switch would then run
`allTaskFail` over the *stale* task and drop that live statement's temp
partitions, after its rows were committed. Recorded because removing the stale
entry is the smaller half of the cleanup.
### Remote target (`RemoteOlapTable`, `RemoteOlapInsertExecutor`,
`RemoteInsertOverwriteManager`)
6. **A lost commit reply** — confirmed as a reachable state, pending as a
reproduction. `masterCallWithRetry` can exhaust its retries *after* the owning
FE committed; this FE then cannot tell a committed transaction from a failed
one, and `onFail`'s abort cannot take a committed remote transaction back — so
the overwrite rolls back temp partitions holding durable rows. Note that the
remote commit request does **not** carry stream updates, so the offset half of
this needs its own reproduction before it is claimed.
7. **A cancellation arriving while the owner waits for the table write
lock** in `replacePartitionsImpl` — confirmed as a protocol gap: the
replacement RPC carries no cancellation state, so an overwrite whose insert
committed nothing can still publish an empty result and report success.
8. **An owner-side crash or master switch** — the same shape as 1 and 2, but
on the frontend that owns the swap.
### Cloud
9. **An uncertain commit** — `KV_TXN_COMMIT_ERR` arrives after the finite
`KV_TXN_MAYBE_COMMITTED` retries, with the meta service having possibly applied
the commit; `commitAndPublishTransactionWithRetry` then throws and the
overwrite treats it as a failure, dropping temp partitions whose rows *and
whose stream offsets* the meta service may already hold.
10. Cloud has no FE publication phase
(`CloudGlobalTransactionMgr.commitAndPublishTransaction` returns true after the
commit), so the publication-timeout shape does not exist there — which is why
the regression case added in #68662 is guarded by `!isCloudMode()`.
### External / plugin targets
11. For a non-OLAP target the FE creates no temp partitions at all ("Do not
create temp partition on FE", `partitionNames` empty) and the two phases belong
to the connector. Whether the same commit/publish split exists there is **not
established** here.
### The uncertain-commit half is not specific to INSERT OVERWRITE
The cloud half is a cloud-transaction-semantics question rather than an
overwrite one, and `INSERT INTO` reaches the same state — see the thread on
`OlapInsertExecutor`. What differs is the direction, and it is worth stating
explicitly because it is why only the overwrite is silent:
- `INSERT INTO` commits its rows into the target's own partitions and
attaches the stream offsets to **the same transaction**, so an uncertain commit
is all-of-it or none-of-it. The symptom is a **false failure**: the client is
told the load failed while the rows and the offsets are durable, so a retry
duplicates. There is no second phase that can drop what was committed.
- The loss in `INSERT OVERWRITE` comes from the combination: rows committed
into **temporary** partitions, which the failure path drops, with the offsets
advanced by that same transaction.
That places the cloud part in the transaction layer (the owner has to be
able to answer whether the commit happened), which is what #68662's scope note
already concludes; the overwrite-specific part is what the other ten paths have
in common.
### Related
- #65265 (binlog umbrella), #65418 (incremental-computation tracker) — the
IVM side of this.
- #68662 — fixes the local cancellation and error-response halves.
- #68390 — the IVM partition refresh journals a rebuild requirement before
it reads, which covers the failure and master-switch halves for a refresh; the
refresh executes through this same two-phase command. Its local
`replacePartition`-failure and crash windows are pinned by
`mtmv_p0/ivm/test_ivm_overwrite_failure_between_the_halves`.
--
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]