Doris-Breakwater commented on issue #67120:
URL: https://github.com/apache/doris/issues/67120#issuecomment-5408102659

   Breakwater-GitHub-Analysis-Slot: slot_4df113a889fe
   
   ## Initial triage
   
   **Verdict: confirmed by static code inspection.** This is a high-priority 
correctness issue in the Shared-Nothing CCR `IngestBinlog` path for UNIQUE KEY 
merge-on-write tablets. A runtime reproduction was not provided or executed, 
but the control flow is sufficient to confirm that an asynchronous 
delete-bitmap failure can be lost and an incomplete bitmap can be attached to a 
committed rowset.
   
   The issue currently has no labels. Please classify it under the repository's 
bug/CCR/MOW conventions and treat it as a data-correctness fix. The same 
discarded-status and cleanup-capture patterns are present in the locally 
configured `branch-3.1` (available 3.1.4-rc02 snapshot), `branch-4.0`, 
`branch-4.1`, and `master` refs, so maintained-branch backports should be 
considered after the primary fix.
   
   ## Verified code path
   
   1. In `_ingest_binlog()` (`be/src/service/backend_service.cpp`), both 
`BaseTablet::commit_phase_update_delete_bitmap(...)` and 
`calc_delete_bitmap_token->wait()` have their returned `Status` explicitly 
discarded. Execution then reaches `TxnManager::commit_txn()` and, for MOW, 
`set_txn_related_delete_bitmap()`.
   2. `CalcDeleteBitmapToken` records a worker failure in `_status`; `wait()` 
is the operation that returns that failure to the caller. The worker log 
therefore does not stop `IngestBinlog` when the wait result is ignored.
   3. `BaseTablet::commit_phase_update_delete_bitmap()` snapshots the current 
historical rowset IDs into `pre_rowset_ids` after task submission, before 
asynchronous completion is known. If a worker later fails, publish receives an 
incomplete bitmap together with a snapshot saying those historical rowsets were 
already processed.
   4. Publish-time `BaseTablet::update_delete_bitmap()` calculates only the 
difference between the then-current rowset IDs and `txn_info->rowset_ids`. It 
does not revisit historical rowsets already present in the commit-phase 
snapshot. The multi-segment fallback only repairs bitmap calculation between 
segments of the new rowset. This confirms the reported window in which missing 
historical-key deletions can survive publication.
   5. A synchronous submission failure is also incorrectly masked. Because 
`pre_rowset_ids` is assigned only after successful submission, publish may 
recalculate more work in that particular case, but relying on that fallback 
does not provide the required transaction atomicity or error reporting.
   6. The ordinary load path does propagate these errors: 
`BaseRowsetBuilder::submit_calc_delete_bitmap_task()` returns the commit-phase 
status, and `wait_calc_delete_bitmap()` returns the token's wait status.
   7. The deferred error handler is created while `download_success_files` is 
empty and captures it through `[=]`. Its cleanup loop therefore sees the 
initial empty copy rather than files appended later. This independently 
confirms the cleanup gap.
   
   ## Recommended fix and tests
   
   - Check and propagate the status from `commit_phase_update_delete_bitmap()` 
immediately. Then check and propagate `calc_delete_bitmap_token->wait()`. On 
either error, set `tstatus` and return before `commit_txn()`; the existing 
deferred handler will then abort the PREPARED tablet transaction.
   - Capture `download_success_files` by reference in the deferred handler so 
the failure path deletes the files accumulated during this ingest. Also log a 
`batch_delete()` failure instead of reporting cleanup as completed 
unconditionally.
   - Extend 
`regression-test/suites/ccr_mow_syncer_p0/test_ingest_binlog.groovy` with a 
target MOW table that already contains the same key. Use the existing 
`BaseTablet::calc_segment_delete_bitmap.inject_err` debug point on the target 
BE to force an asynchronous failure, and verify:
     - ingest reports failure (or the asynchronous query state becomes 
`FAILED`);
     - the target transaction is not committed/published and the failed version 
is not visible;
     - downloaded segment/index files from the failed attempt are removed;
     - after disabling the debug point, retrying the same binlog succeeds and 
the target exposes exactly one value for the unique key.
   - Add separate coverage for task-submission rejection if practical, because 
the existing debug point exercises worker execution failure rather than 
`submit()` failure.
   - Backport the fix to affected maintained branches after validation.
   
   ## Missing incident evidence
   
   The code defect can be fixed without additional incident data. To confirm 
whether a specific deployment already hit it, please provide the exact BE build 
commit, target table DDL, target tablet/transaction/binlog version identifiers, 
the target BE log span containing `failed to calc segment delete bitmap` and 
the final `ingest binlog elapsed ... result`, CCR/FE commit-and-publish logs 
for the same transaction, and a minimal before/after query showing the 
duplicate key. Memory-arbitration logs around the failure would confirm the 
proposed low-water-mark trigger; that trigger is plausible but is not 
established by the current issue data.
   


-- 
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]

Reply via email to