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

Siyao Meng updated HDDS-17735:
------------------------------
    Affects Version/s: 2.2.1
                       2.2.2
                       2.2.0

> 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
>    Affects Versions: 2.2.0, 2.2.1, 2.2.2
>            Reporter: Siyao Meng
>            Priority: Minor
>         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