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

Siyao Meng updated HDDS-16444:
------------------------------
    Description: 
h3. Finding
A concurrent cancelSnapshotDiff makes the diff task's catch-block 
updateJobStatusToFailed throw IllegalStateException, so the genuine 
storage-layer failure cause is never logged and a misleading exception escapes 
the worker; the terminal job state stays correct (CANCELLED) and cleanup runs, 
so the impact is diagnostic/robustness only on the operator log surface.

h3. Classification
* Verdict: REPRODUCED
* Severity: Low
* Source: Specula TLA+ model checking and confirmation debate, finding CR-3

h3. Reproduce
{noformat}
Ozone commit: 9fbf9ee0cb1bd2f5f5d437b6719ebbe5309351fb
Specula:      v1.1.0 (commit c6aa3dfa)
Target:       om-snapshot-diff-jobs
Guidance:     
campaigns/ozone-9fbf9ee/targets/009-om-snapshot-diff-jobs/.prompt-extra.md
{noformat}
{code:none}
specula run --agent=claude-code --effort=high --keep-original --max-parallel=2 \
  --enable-reviews --confirm-debate --tlc-memory-limit=28G --tlc-worker-limit=8 
\
  "om-snapshot-diff-jobs|apache/ozone|Java|Use the target-specific 
.prompt-extra.md"
{code}
Discovered under HDDS-16430 (Specula TLA+ verification effort). The TLA+ 
specification, counterexample, and confirmation debate live in the Specula run 
artifacts.

Generated with Specula (Claude Opus 4.8).

h3. Steps to reproduce

1. The snapshot diff executor runs generateSnapshotDiffReport for an 
IN_PROGRESS job; a guard check passes.
2. A client calls cancelSnapshotDiff; updateJobStatus commits IN_PROGRESS to 
CANCELLED on a separate thread.
3. The in-flight diff step throws a real checked exception (I/O or RocksDB 
error during delta or report generation).
4. The catch block calls updateJobStatusToFailed first. The job status is now 
CANCELLED, not IN_PROGRESS, so updateJobStatusToFailed throws 
IllegalStateException.
5. That IllegalStateException is thrown before LOG.error(..., exception) and 
before the wrap-and-rethrow, so the original failure cause is neither logged 
nor propagated. The worker dies with a misleading IllegalStateException (cause 
null) and the operator's log never records the genuine storage-layer fault.

The terminal job state remains correctly CANCELLED, so the impact is diagnostic 
and robustness only.

h3. Root cause

hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java:
 updateJobStatusToFailed unconditionally threw IllegalStateException whenever 
the current status was not IN_PROGRESS, and both catch blocks in 
generateSnapshotDiffReport called it before logging the original exception. A 
concurrent cancel that reaches CANCELLED first therefore turns a diff failure 
into an unrelated IllegalStateException that hides the real cause.

h3. Suggested fix

Make updateJobStatusToFailed tolerate a concurrent terminal transition: if the 
job is no longer IN_PROGRESS (or the row is gone), log a warning and return 
instead of throwing, leaving the terminal state intact. Move LOG.error(..., 
exception) ahead of the status update in both catch blocks so the true cause is 
always recorded before any status change. A unit test drives the real 
generateSnapshotDiffReport catch path with a concurrent cancel and asserts the 
escaping exception wraps the original IOException, the cause is logged, and the 
job stays CANCELLED. See [^HDDS-16444.001.patch].

Patch is an AI-drafted proposal (Specula + Claude); pending human build, full 
test, and review before merge.


  was:
h3. Finding
A concurrent cancelSnapshotDiff makes the diff task's catch-block 
updateJobStatusToFailed throw IllegalStateException, so the genuine 
storage-layer failure cause is never logged and a misleading exception escapes 
the worker; the terminal job state stays correct (CANCELLED) and cleanup runs, 
so the impact is diagnostic/robustness only on the operator log surface.

h3. Classification
* Verdict: REPRODUCED
* Severity: Low
* Source: Specula TLA+ model checking and confirmation debate, finding CR-3

h3. Reproduce
{noformat}
Ozone commit: 9fbf9ee0cb1bd2f5f5d437b6719ebbe5309351fb
Specula:      v1.1.0 (commit c6aa3dfa)
Target:       om-snapshot-diff-jobs
Guidance:     
campaigns/ozone-9fbf9ee/targets/009-om-snapshot-diff-jobs/.prompt-extra.md
{noformat}
{code:none}
specula run --agent=claude-code --effort=high --keep-original --max-parallel=2 \
  --enable-reviews --confirm-debate --tlc-memory-limit=28G --tlc-worker-limit=8 
\
  "om-snapshot-diff-jobs|apache/ozone|Java|Use the target-specific 
.prompt-extra.md"
{code}
Discovered under HDDS-16430 (Specula TLA+ verification effort). The TLA+ 
specification, counterexample, and confirmation debate live in the Specula run 
artifacts.

Generated with Specula (Claude Opus 4.8).


> Concurrent cancelSnapshotDiff hides the real failure cause with an 
> IllegalStateException
> ----------------------------------------------------------------------------------------
>
>                 Key: HDDS-16444
>                 URL: https://issues.apache.org/jira/browse/HDDS-16444
>             Project: Apache Ozone
>          Issue Type: Bug
>            Reporter: Siyao Meng
>            Priority: Minor
>         Attachments: HDDS-16444.001.patch
>
>
> h3. Finding
> A concurrent cancelSnapshotDiff makes the diff task's catch-block 
> updateJobStatusToFailed throw IllegalStateException, so the genuine 
> storage-layer failure cause is never logged and a misleading exception 
> escapes the worker; the terminal job state stays correct (CANCELLED) and 
> cleanup runs, so the impact is diagnostic/robustness only on the operator log 
> surface.
> h3. Classification
> * Verdict: REPRODUCED
> * Severity: Low
> * Source: Specula TLA+ model checking and confirmation debate, finding CR-3
> h3. Reproduce
> {noformat}
> Ozone commit: 9fbf9ee0cb1bd2f5f5d437b6719ebbe5309351fb
> Specula:      v1.1.0 (commit c6aa3dfa)
> Target:       om-snapshot-diff-jobs
> Guidance:     
> campaigns/ozone-9fbf9ee/targets/009-om-snapshot-diff-jobs/.prompt-extra.md
> {noformat}
> {code:none}
> specula run --agent=claude-code --effort=high --keep-original 
> --max-parallel=2 \
>   --enable-reviews --confirm-debate --tlc-memory-limit=28G 
> --tlc-worker-limit=8 \
>   "om-snapshot-diff-jobs|apache/ozone|Java|Use the target-specific 
> .prompt-extra.md"
> {code}
> Discovered under HDDS-16430 (Specula TLA+ verification effort). The TLA+ 
> specification, counterexample, and confirmation debate live in the Specula 
> run artifacts.
> Generated with Specula (Claude Opus 4.8).
> h3. Steps to reproduce
> 1. The snapshot diff executor runs generateSnapshotDiffReport for an 
> IN_PROGRESS job; a guard check passes.
> 2. A client calls cancelSnapshotDiff; updateJobStatus commits IN_PROGRESS to 
> CANCELLED on a separate thread.
> 3. The in-flight diff step throws a real checked exception (I/O or RocksDB 
> error during delta or report generation).
> 4. The catch block calls updateJobStatusToFailed first. The job status is now 
> CANCELLED, not IN_PROGRESS, so updateJobStatusToFailed throws 
> IllegalStateException.
> 5. That IllegalStateException is thrown before LOG.error(..., exception) and 
> before the wrap-and-rethrow, so the original failure cause is neither logged 
> nor propagated. The worker dies with a misleading IllegalStateException 
> (cause null) and the operator's log never records the genuine storage-layer 
> fault.
> The terminal job state remains correctly CANCELLED, so the impact is 
> diagnostic and robustness only.
> h3. Root cause
> hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java:
>  updateJobStatusToFailed unconditionally threw IllegalStateException whenever 
> the current status was not IN_PROGRESS, and both catch blocks in 
> generateSnapshotDiffReport called it before logging the original exception. A 
> concurrent cancel that reaches CANCELLED first therefore turns a diff failure 
> into an unrelated IllegalStateException that hides the real cause.
> h3. Suggested fix
> Make updateJobStatusToFailed tolerate a concurrent terminal transition: if 
> the job is no longer IN_PROGRESS (or the row is gone), log a warning and 
> return instead of throwing, leaving the terminal state intact. Move 
> LOG.error(..., exception) ahead of the status update in both catch blocks so 
> the true cause is always recorded before any status change. A unit test 
> drives the real generateSnapshotDiffReport catch path with a concurrent 
> cancel and asserts the escaping exception wraps the original IOException, the 
> cause is logged, and the job stays CANCELLED. See [^HDDS-16444.001.patch].
> Patch is an AI-drafted proposal (Specula + Claude); pending human build, full 
> test, and review before merge.



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