[ 
https://issues.apache.org/jira/browse/HDDS-17040?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Siyao Meng updated HDDS-17040:
------------------------------
    Attachment: CR-8-retry-delta-task-that-throws.patch

> Recon delta task that throws is not retried and triggers no reinitialization, 
> so its batch is silently skipped
> --------------------------------------------------------------------------------------------------------------
>
>                 Key: HDDS-17040
>                 URL: https://issues.apache.org/jira/browse/HDDS-17040
>             Project: Apache Ozone
>          Issue Type: Bug
>            Reporter: Siyao Meng
>            Priority: Minor
>         Attachments: CR-8-retry-delta-task-that-throws.patch, 
> TestBugCR8ThrowingTaskLosesBatch.java
>
>
> h3. Mechanism
> {{ReconTaskControllerImpl.processTasks}} runs each task's {{process}} on the 
> task pool. A task that returns a failed result goes through {{thenAccept}}: 
> it is added to {{failedTasks}}, retried once, and if the retry fails too 
> {{tasksFailed}} is set, which makes the next sync queue a reinitialization. A 
> task whose {{process}} throws goes through {{exceptionally}} instead, which 
> logs, counts the failure and records status -1 but does not add the task to 
> {{failedTasks}}. So there is no retry and no reinitialization, and the next 
> batch the task processes successfully sets its {{lastUpdatedSeqNumber}} past 
> the lost batch. The javadoc of {{processTasks}} says failed tasks are added 
> to the list for retry or reprocessing.
> h3. Trigger
> Any unchecked exception out of a task's {{process}} that does not also close 
> the Recon DB. I did not find a production condition that does this, so in 
> practice this is a safety net for unexpected runtime exceptions such as a bug 
> in a handler. The reproduction injects the exception. If one occurs, it would 
> surface where {{FileSizeCountTaskHelper.writeCountsToDB}} rethrows a write 
> failure as {{RuntimeException}}, or where 
> {{ContainerKeyMapperHelper.process}} lets anything other than an 
> {{IOException}} from its key handlers escape. {{NSSummaryTask}} and 
> {{OmTableInsightTask}} catch per event and return a failed result, so they 
> are not affected. A RocksDB I/O error is not a trigger: it closes the Recon 
> DB, the other tasks then fail as well and the reinitialization they cause 
> also rebuilds the throwing task (code reading).
> h3. Impact
> The batch is missing from the derived data of the throwing task for good, for 
> example from the file size distribution served by the utilization endpoint. 
> Nothing detects it: the task cursor is current after the next batch, 
> {{tasksFailed}} stays false and the startup check sees no gap. A manual 
> rebuild repairs it.
> h3. Reproduction
> PASS, deterministic, unmodified source. 
> [^TestBugCR8ThrowingTaskLosesBatch.java] ({{hadoop-ozone/recon}}) uses the 
> real controller, {{FileSizeCountTaskOBS}}, the RocksDB backed 
> {{ReconFileMetadataManager}} and the task status table, and injects one 
> {{IOException}} from the first commit of the file size counts, so {{process}} 
> throws once. Two batches are handed to the controller's event processing one 
> after the other. Output: one {{process}} call for the first batch, no 
> reinitialization requested, cursor at the second batch, and the 1000 byte 
> file of the first batch missing from the counts. A passing test means the 
> defect is present. The injected exception stands in for an unchecked 
> exception that does not close the DB.
> h3. Patch
> [^CR-8-retry-delta-task-that-throws.patch], against 
> 0665932dd35da4d05087e02748af0a64eb846ad5. It also applies to master at 
> 2264d4d48ac.
> The {{exceptionally}} branch adds the task to {{failedTasks}} without seek 
> positions, so a thrown exception is handled like a returned failure: one 
> retry of the batch, then a reinitialization if it fails again. 
> {{failedTasks}} and {{retryFailedTasks}} become synchronized lists, since 
> both branches add to them from pool threads. A task that throws on every 
> attempt now leads to a reinitialization of all tasks instead of a silently 
> skipped batch, as a task that returns failure already does.
> Covered in the existing {{TestReconTaskControllerImpl}}: 
> {{testTaskRecordsFailureOnException}} asserted the single call and now 
> asserts the retry and the reinitialization request, and the new 
> {{testTaskThrowingOnceIsRetried}} fails without the change ({{expected: 100L 
> but was: 0L}}). With the change the Recon tasks suites and 
> {{TestOzoneManagerServiceProviderImpl}} (155 tests) pass and checkstyle is 
> clean. With the patch the reproduction fails at its first assertion: the task 
> is retried and the file is counted.
> Found by code review of the Recon namespace summary and task sync paths, as 
> part of the TLA+ verification effort under HDDS-15926, on commit 
> 0665932dd35da4d05087e02748af0a64eb846ad5. Checked against HDDS issues and 
> apache/ozone pull requests for duplicates before filing. Related but not a 
> duplicate: HDDS-16463 (sync the Recon DB before the cursor advances, which 
> reworks the success branch of the same method). The attached patch is a 
> proposal for review. Generated with Specula (Claude Opus 5.5).



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to