Siyao Meng created HDDS-17735:
---------------------------------

             Summary: Snapshot diff cleanup can delete a job resubmitted during 
its scan, failing the submit or losing the new job
                 Key: HDDS-17735
                 URL: https://issues.apache.org/jira/browse/HDDS-17735
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: MC-6-keep-job-resubmitted-during-cleanup-scan.patch, 
TestBugMC6CleanupDeletesResubmittedJob.java

h3. Mechanism
{{SnapshotDiffCleanupService.moveOldSnapDiffJobsToPurgeTable}} iterates the 
snapshot diff job table, and for every expired or CANCELLED, FAILED or REJECTED 
job it queues a delete of that job key in a write batch, which it writes after 
the scan. It does not read the row again before the write and takes no lock 
that {{SnapshotDiffManager}} takes. A resubmit of the same snapshot pair in 
{{SnapshotDiffManager.createSnapDiffJob}} stores a new job (new job ID, QUEUED) 
under the same job key. If that happens after the cleanup read the old row and 
before it wrote the batch, the batch deletes the new job.

This is the same symptom as HDDS-16443, through a different cause. HDDS-16443 
describes a new job created with the previous terminal status, so that a fresh 
scan purges it. That stated cause is already closed on master by HDDS-15397, 
which creates the new job as QUEUED; it does not close this one, where the scan 
is stale.

h3. Trigger
# A snapshot diff job ends CANCELLED, FAILED or REJECTED.
# A cleanup run scans the job table and reads that job.
# Before the cleanup writes its batch, a client submits the same pair again.

No fault is needed. The window is the duration of the cleanup's scan, once per 
cleanup interval (default 60 minutes).

h3. Impact
* If the batch lands between the creation of the new job and its switch to 
IN_PROGRESS in {{submitSnapshotDiff}}, {{updateJobStatus}} and then 
{{updateJobStatusToFailed}} hit the missing row and the submit RPC fails with a 
NullPointerException.
* If it lands after the submit returned, the client was told the job was 
submitted but the job is gone: {{cancelSnapshotDiff}} answers that the job does 
not exist, and the next submit starts a new job, so the diff work is done again.

In a test harness with the cleanup running back to back on its own thread, 482 
of 20,000 submit and cancel iterations failed with the NullPointerException and 
1 job was lost after a successful submit; in production the cleanup scans once 
per interval, so the rate is far lower.

In the lost job case the job's task keeps running and writes report rows under 
the lost job ID: its whole report if a new submit created a job under the same 
key before its next {{areDiffJobAndSnapshotsActive}} check, otherwise the rows 
written up to that check. Nothing queues the lost job ID for purge (the cleanup 
queued the old job ID it scanned, and the next submit finds no previous job), 
so these rows stay in the OM's local snapshot diff DB. This is read from the 
code, not run. The fix proposed in the related issue on report rows of a 
cancelled task, which queues a task's job ID for cleanup when its job row is 
gone or replaced at the end of the task, would also purge these rows. No user 
data is affected.

This is less severe than HDDS-16443 suggests: the window is one stale scan per 
cleanup interval, and a client retry recovers.

h3. Reproduction
PASS, deterministic, unmodified source. 
[^TestBugMC6CleanupDeletesResubmittedJob.java] ({{hadoop-ozone/ozone-manager}}) 
runs the real {{SnapshotDiffManager}} and {{SnapshotDiffCleanupService}} on a 
real RocksDB with the rest of the OM mocked; the report generation task does 
nothing, so a submitted job stays IN_PROGRESS like a long running diff. The 
only timing aid is a hook on the cleanup's codec: when the cleanup decodes the 
job it scanned, the hook starts the resubmit on another thread and waits until 
it has finished or is blocked. Output on 
d3b75c75bf9ad475551202477c3a21a3f4b968b7:
{noformat}
resubmit response: Previous snapshot diff attempt found. Status: CANCELLED.
job after one cleanup run: null
cancel of the resubmitted job: Snapshot diff job doesn't exist for given 
parameters.
{noformat}
A passing test means the defect is present.

h3. Patch
[^MC-6-keep-job-resubmitted-during-cleanup-scan.patch], against 
d3b75c75bf9ad475551202477c3a21a3f4b968b7. It also applies to master at 
bbc8505e00d.

{{SnapshotDiffCleanupService.run}} calls {{moveOldSnapDiffJobsToPurgeTable}} 
inside {{synchronized (db)}}, and {{SnapshotDiffManager.createSnapDiffJob}} 
writes the new job inside the same monitor. The cleanup's scan and its delete 
are then atomic with respect to the resubmit's write, so the cleanup either 
sees the new job (not purgeable) or removes the old one before the new one is 
written. The monitor is the shared {{ManagedRocksDB}} instance, the same one 
the open PR for HDDS-16442 uses in {{run}}, so the two changes compose (they 
touch the same lines of {{run}} and merge into one block). The cost: job 
creation, and the progress and status updates of running diff tasks that wait 
for the manager monitor meanwhile, wait for one scan of the job table per 
cleanup run.

Covered by {{testCleanupKeepsJobResubmittedDuringItsScan}} in the existing 
{{TestSnapshotDiffManager}}, which uses the same codec hook and asserts that 
the resubmitted job survives with a new job ID while the cancelled job is still 
queued for purge. Without the change it fails because the job is gone. With it 
{{TestSnapshotDiffManager}}, {{TestSnapshotDiffCleanupService}} and 
{{TestSnapshotDiffManagerMXBean}} (88 tests) pass, checkstyle is clean, the 
stress run above completes with no failed submit and no lost job, and the 
reproduction fails because the job is present. The released 2.2 line also has 
the HDDS-16443 cause, which this patch does not address there.

Found by TLA+ model checking and code review of the OM optimized snapshot diff 
paths under HDDS-15926, on commit d3b75c75bf9ad475551202477c3a21a3f4b968b7. 
Checked against HDDS issues and apache/ozone pull requests for duplicates 
before filing. The attached patch is a proposal for review. Generated with 
Specula (Claude Opus 5.5).



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

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

Reply via email to