smengcl commented on code in PR #11651:
URL: https://github.com/apache/ozone/pull/11651#discussion_r4332761247
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java:
##########
@@ -656,48 +677,83 @@ public synchronized SubmitSnapshotDiffResponse
submitSnapshotDiff(
SnapshotDiffJob snapDiffJob = getSnapDiffReportStatus(snapDiffJobKey,
volumeName, bucketName, fromSnapshotName, toSnapshotName,
- forceFullDiff, disableNativeDiff);
+ forceFullDiff, disableNativeDiff, orderedReport);
JobStatus prevStatus = snapDiffJob.getStatus();
String prevReason = isEmpty(snapDiffJob.getReason()) ? null :
snapDiffJob.getReason();
String jobId = snapDiffJob.getJobId();
+ boolean createNewJob = false;
+ boolean submitJob = false;
+ boolean isFso = getBucketLayout(volumeName, bucketName,
activeOmMetadataManager).isFileSystemOptimized();
+ String responseMsg = "";
+
if (prevStatus == FAILED || prevStatus == REJECTED || prevStatus ==
CANCELLED) {
+ createNewJob = true;
+ submitJob = true;
+ } else if ((prevStatus == QUEUED || prevStatus == IN_PROGRESS ||
prevStatus == DONE) &&
Review Comment:
This also supersedes a job that is still IN_PROGRESS, but nothing stops the
old task:
- `areDiffJobAndSnapshotsActive` only checks the status under `jobKey`, and
the new job is IN_PROGRESS too, so the old task runs to completion and keeps an
executor slot.
- `recordActivity` and `updateProgress` are keyed by `jobKey` with no jobId
check, so the old task overwrites the new job's subStatus and progress.
- `createSnapDiffJob` enqueues the old jobId for purge right away. If
`SnapshotDiffCleanupService#removeOlderJobReport` runs before the old task
writes its report, the rows it writes afterwards are never cleaned up.
Simplest is to only supersede a QUEUED or DONE job; an IN_PROGRESS one then
falls through to the usual "get the report in N ms" response, and the next
`--ordered` submit after it is DONE reruns it. A test resubmitting `--ordered`
against an IN_PROGRESS job would be good too.
```suggestion
} else if ((prevStatus == QUEUED || prevStatus == DONE) &&
```
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/diff/MergeJoinSnapDiffWriter.java:
##########
@@ -121,11 +147,37 @@ private static void classifyMergeJoin(SnapDiffJobStore
store, boolean isFso) thr
newEntry = newHead.hasNext() ? newHead.next() : null;
oldEntry = oldHead.hasNext() ? oldHead.next() : null;
}
+ mergeSteps++;
+ SnapDiffProgressHelper.reportSlice(reportProgress, 0.0, classifyEnd,
Review Comment:
`reportProgress` ends up in `SnapshotDiffManager#updateProgress`, which is a
synchronized get, serialize and put on `snapDiffJobTable`. Calling it on every
merge step means one RocksDB write per entry, each taking the manager lock that
submit and get report also need. Could this use `TenPercentCheckpoint` like the
scan readers?
```diff
long mergeSteps = 0L;
long mergeDenominator = store.mergeJoinStepDenominator();
double classifyEnd = isFso ? FSO_CLASSIFY_END : 1.0;
+ SnapDiffProgressHelper.TenPercentCheckpoint checkpoint = new
SnapDiffProgressHelper.TenPercentCheckpoint();
...
mergeSteps++;
- SnapDiffProgressHelper.reportSlice(reportProgress, 0.0, classifyEnd,
- (double) mergeSteps / mergeDenominator);
+ SnapDiffProgressHelper.reportEveryTenPercentInSlice(reportProgress,
0.0, classifyEnd, mergeSteps,
+ mergeDenominator, checkpoint);
```
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/diff/MergeJoinSnapDiffWriter.java:
##########
@@ -121,11 +147,37 @@ private static void classifyMergeJoin(SnapDiffJobStore
store, boolean isFso) thr
newEntry = newHead.hasNext() ? newHead.next() : null;
oldEntry = oldHead.hasNext() ? oldHead.next() : null;
}
+ mergeSteps++;
+ SnapDiffProgressHelper.reportSlice(reportProgress, 0.0, classifyEnd,
+ (double) mergeSteps / mergeDenominator);
}
} catch (RocksDatabaseException e) {
throw new IOException(e);
}
store.flushWrites();
+ if (reportProgress != null) {
+ reportProgress.accept(isFso ? FSO_CLASSIFY_END : 1.0);
+ }
+ }
+
+ private static final class ResolveProgress {
+ private final DoubleConsumer reportProgress;
+ private final long resolveDenominator;
+ private final double resolveEnd;
+ private long resolveSteps;
+
+ ResolveProgress(SnapDiffJobStore store, DoubleConsumer reportProgress,
+ boolean dependencyOrderingEnabled) {
+ this.reportProgress = reportProgress;
+ this.resolveDenominator = store.classifiedEntryDenominator();
+ this.resolveEnd = dependencyOrderingEnabled ? FSO_RESOLVE_END_ORDERED :
FSO_RESOLVE_END_UNORDERED;
+ }
+
+ void onClassifiedEntry(DiffType diffType) {
+ resolveSteps++;
+ SnapDiffProgressHelper.reportSlice(reportProgress, FSO_CLASSIFY_END,
resolveEnd,
+ (double) resolveSteps / resolveDenominator);
Review Comment:
Same here, this writes job progress to RocksDB for every classified entry.
With a `TenPercentCheckpoint` field on `ResolveProgress`:
```suggestion
SnapDiffProgressHelper.reportEveryTenPercentInSlice(reportProgress,
FSO_CLASSIFY_END, resolveEnd,
resolveSteps, resolveDenominator, checkpoint);
```
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java:
##########
@@ -656,48 +677,83 @@ public synchronized SubmitSnapshotDiffResponse
submitSnapshotDiff(
SnapshotDiffJob snapDiffJob = getSnapDiffReportStatus(snapDiffJobKey,
volumeName, bucketName, fromSnapshotName, toSnapshotName,
- forceFullDiff, disableNativeDiff);
+ forceFullDiff, disableNativeDiff, orderedReport);
JobStatus prevStatus = snapDiffJob.getStatus();
String prevReason = isEmpty(snapDiffJob.getReason()) ? null :
snapDiffJob.getReason();
String jobId = snapDiffJob.getJobId();
+ boolean createNewJob = false;
+ boolean submitJob = false;
+ boolean isFso = getBucketLayout(volumeName, bucketName,
activeOmMetadataManager).isFileSystemOptimized();
+ String responseMsg = "";
+
if (prevStatus == FAILED || prevStatus == REJECTED || prevStatus ==
CANCELLED) {
+ createNewJob = true;
+ submitJob = true;
+ } else if ((prevStatus == QUEUED || prevStatus == IN_PROGRESS ||
prevStatus == DONE) &&
+ (isFso && orderedReport && !snapDiffJob.isOrderedReport())) {
+ createNewJob = true;
+ submitJob = true;
+ responseMsg = "Previous snapshot diff attempt found with an unordered
report. Status: " + prevStatus
+ + ".\nSubmitting a new job with ordered report. Please retry after "
+ defaultWaitTime +
+ " ms using --get-report option to get the report.\n";
+ } else if (prevStatus == QUEUED) {
+ submitJob = true;
+ }
+
+ if (createNewJob) {
jobId = createSnapDiffJob(snapDiffJobKey, volumeName, bucketName,
fromSnapshotName,
- toSnapshotName, forceFullDiff, disableNativeDiff, prevStatus,
snapDiffJob);
+ toSnapshotName, forceFullDiff, disableNativeDiff, orderedReport,
QUEUED, snapDiffJob);
}
final String submitJobId = jobId;
- switch (prevStatus) {
- case QUEUED:
- case FAILED:
- case REJECTED:
- case CANCELLED:
+ if (submitJob) {
LOG.info("Submitting snapshot diff generation request for" +
" volume: {}, bucket: {}, fromSnapshot: {} and toSnapshot: {}",
volumeName, bucketName, fromSnapshotName, toSnapshotName);
try {
- updateJobStatus(snapDiffJobKey, prevStatus, IN_PROGRESS);
+ updateJobStatus(snapDiffJobKey, QUEUED, IN_PROGRESS);
snapDiffExecutor.execute(() ->
generateSnapshotDiffReport(snapDiffJobKey, submitJobId,
volumeName, bucketName, fromSnapshotName, toSnapshotName,
- forceFullDiff, disableNativeDiff));
- return new SubmitSnapshotDiffResponse(defaultWaitTime, prevStatus,
prevReason);
+ forceFullDiff, disableNativeDiff, orderedReport));
} catch (RejectedExecutionException exception) {
updateJobStatus(snapDiffJobKey, IN_PROGRESS, REJECTED);
LOG.warn("Exceeded the snapDiff parallel requests processing " +
- "limit. Rejecting the snapDiff job: {}.", snapDiffJobKey);
- return new SubmitSnapshotDiffResponse("Snapshot diff job was rejected
because parallel processing of " +
- "snap diff jobs exceeded the limit. Please try again in " +
defaultWaitTime + " ms.");
+ "limit. Rejecting the snapDiff job: {}.", snapDiffJobKey);
+ responseMsg = "Snapshot diff job was rejected because parallel
processing of " +
+ "snap diff jobs exceeded the limit. Please try again in " +
defaultWaitTime + " ms.";
} catch (Exception exception) {
updateJobStatusToFailed(snapDiffJobKey, exception.getMessage());
LOG.error("Snapshot diff job: {} failed.", snapDiffJobKey, exception);
- return new SubmitSnapshotDiffResponse("Snapshot diff job submission
failed. Reason: " + exception.getMessage());
+ responseMsg = "Snapshot diff job submission failed. Reason: " +
exception.getMessage();
}
- case IN_PROGRESS:
- case DONE:
- return new SubmitSnapshotDiffResponse(defaultWaitTime, prevStatus,
prevReason);
- default:
- throw new IllegalStateException("Unknown snapshot job status: " +
snapDiffJob.getStatus());
}
+ if (!isEmpty(responseMsg)) {
+ return new SubmitSnapshotDiffResponse(responseMsg);
+ }
+ return new SubmitSnapshotDiffResponse(defaultWaitTime, prevStatus,
prevReason);
+ }
+
+ private SnapshotDiffResponse newDoneSnapshotDiffResponse(SnapshotDiffJob
snapDiffJob,
+ String volumeName, String bucketName, String fromSnapshotName, String
toSnapshotName,
+ String index, int pageSize) throws IOException {
+ SnapshotDiffReportOzone report = createPageResponse(snapDiffJob,
volumeName, bucketName,
+ fromSnapshotName, toSnapshotName, index, pageSize);
+ boolean dependencyOrdered = isDependencyOrderedReport(snapDiffJob,
volumeName, bucketName);
+ return new SnapshotDiffResponse(report, DONE, 0L,
dependencyOrderingLine(dependencyOrdered));
+ }
+
+ private boolean isDependencyOrderedReport(SnapshotDiffJob snapDiffJob,
String volumeName,
+ String bucketName) throws IOException {
+ if (getBucketLayout(volumeName, bucketName,
ozoneManager.getMetadataManager())
+ .isFileSystemOptimized()) {
+ return snapDiffJob.isOrderedReport();
Review Comment:
This returns the requested flag, not what was actually produced:
- With `ozone.om.snapshot.diff.optimized.enabled=false` (the default), or
the non native DAG fallback, `generateLegacySnapshotDiffReport` ignores
`orderedReport`. So `--ordered` gives an unordered report labelled "dependency
ordered", and on a DONE FSO job it also triggers a full rerun that produces the
same report.
- `MergeJoinSnapDiffWriter` switches to unordered output when the dependency
node count exceeds `maxInMemoryEntries` (`dependencyOrderingEnabled[0] =
false`), and the job still says ordered.
Could `writeReport` return whether ordering was actually applied
(`dependencyOrderingEnabledRef[0]`) and the job record that on DONE, with the
flag forced to false when the legacy path runs?
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java:
##########
@@ -93,6 +98,7 @@
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
+import java.util.function.DoubleConsumer;
Review Comment:
Import order, checkstyle will flag this. It belongs with the other
`java.util.function` imports:
```diff
import java.util.concurrent.Callable;
-import java.util.function.DoubleConsumer;
import java.util.concurrent.ExecutorService;
...
import java.util.function.BiFunction;
import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
```
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/diff/SnapDiffJobStore.java:
##########
@@ -33,6 +33,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.EnumMap;
import java.util.Comparator;
Review Comment:
Import order:
```suggestion
import java.util.Comparator;
import java.util.EnumMap;
```
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java:
##########
@@ -136,8 +143,13 @@
import org.apache.hadoop.ozone.om.helpers.WithObjectID;
import org.apache.hadoop.ozone.om.helpers.WithParentObjectId;
import org.apache.hadoop.ozone.om.snapshot.db.SnapshotDiffDBDefinition;
+import org.apache.hadoop.ozone.om.snapshot.diff.DagDiffSequentialReader;
+import org.apache.hadoop.ozone.om.snapshot.diff.FullDiffSequentialReader;
+import org.apache.hadoop.ozone.om.snapshot.diff.MergeJoinSnapDiffWriter;
+import org.apache.hadoop.ozone.om.snapshot.diff.SnapDiffJobStore;
import
org.apache.hadoop.ozone.om.snapshot.diff.delta.CompositeDeltaDiffComputer;
import org.apache.hadoop.ozone.om.snapshot.diff.delta.DeltaFileComputer;
+import
org.apache.hadoop.ozone.om.snapshot.OmSnapshotLocalDataManager.ReadableOmSnapshotLocalDataProvider;
Review Comment:
Import order, this should come before `snapshot.db`:
```suggestion
import
org.apache.hadoop.ozone.om.snapshot.OmSnapshotLocalDataManager.ReadableOmSnapshotLocalDataProvider;
import org.apache.hadoop.ozone.om.snapshot.db.SnapshotDiffDBDefinition;
import org.apache.hadoop.ozone.om.snapshot.diff.DagDiffSequentialReader;
import org.apache.hadoop.ozone.om.snapshot.diff.FullDiffSequentialReader;
import org.apache.hadoop.ozone.om.snapshot.diff.MergeJoinSnapDiffWriter;
import org.apache.hadoop.ozone.om.snapshot.diff.SnapDiffJobStore;
import
org.apache.hadoop.ozone.om.snapshot.diff.delta.CompositeDeltaDiffComputer;
import org.apache.hadoop.ozone.om.snapshot.diff.delta.DeltaFileComputer;
```
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/diff/MergeJoinSnapDiffWriter.java:
##########
@@ -27,6 +27,7 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.function.DoubleConsumer;
Review Comment:
Import order, checkstyle will flag this:
```diff
import java.util.ArrayList;
-import java.util.function.DoubleConsumer;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.DoubleConsumer;
```
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/diff/SnapDiffProgressHelper.java:
##########
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.ozone.om.snapshot.diff;
+
+import java.util.function.DoubleConsumer;
+
+/**
+ * Shared 10% checkpoint reporting for snapshot-diff scan stages.
+ */
+public final class SnapDiffProgressHelper {
+
+ private static final double TEN_PCT_STEP = 0.1;
+
+ private SnapDiffProgressHelper() {
+ }
+
+ static void reportSlice(DoubleConsumer progress, double sliceStart, double
sliceEnd,
+ double completedFraction) {
+ if (progress == null) {
+ return;
+ }
+ double width = sliceEnd - sliceStart;
+ progress.accept(sliceStart + width * Math.min(1.0, Math.max(0.0,
completedFraction)));
+ }
+
+ static void reportEveryTenPercent(DoubleConsumer progress, long
processedUnits, long totalUnits,
+ TenPercentCheckpoint checkpoint) {
+ reportEveryTenPercentInSlice(progress, 0.0, 1.0, processedUnits,
totalUnits, checkpoint);
+ }
+
+ static void reportEveryTenPercentInSlice(DoubleConsumer progress, double
sliceStart, double sliceEnd,
+ long processedUnits, long totalUnits, TenPercentCheckpoint checkpoint) {
+ if (progress == null) {
+ return;
+ }
+ long denominator = Math.max(1L, totalUnits);
+ double fraction = (double) processedUnits / denominator;
+ while (fraction >= checkpoint.nextCheckpoint) {
+ reportSlice(progress, sliceStart, sliceEnd, checkpoint.nextCheckpoint);
+ checkpoint.nextCheckpoint += TEN_PCT_STEP;
+ }
+ if (processedUnits >= totalUnits) {
+ reportSlice(progress, sliceStart, sliceEnd, 1.0);
+ }
Review Comment:
Once `processedUnits >= totalUnits`, this reports again on every following
row, and the `while` loop also keeps firing past 1.0. The totals are RocksDB
estimates (`getEstimatedKeyCount`, SST entry counts, clamped to 1), so the real
count can exceed them, and each report is a RocksDB write in `updateProgress`.
Every caller already reports the end of its slice after its loop, so the
trailing report can go:
```suggestion
while (checkpoint.nextCheckpoint <= 1.0 && fraction >=
checkpoint.nextCheckpoint) {
reportSlice(progress, sliceStart, sliceEnd, checkpoint.nextCheckpoint);
checkpoint.nextCheckpoint += TEN_PCT_STEP;
}
```
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java:
##########
@@ -1614,7 +1614,8 @@ private SubmitSnapshotDiffResponse submitSnapshotDiff(
submitSnapshotDiffRequest.getFromSnapshot(),
submitSnapshotDiffRequest.getToSnapshot(),
submitSnapshotDiffRequest.getForceFullDiff(),
- submitSnapshotDiffRequest.getDisableNativeDiff());
+ submitSnapshotDiffRequest.getDisableNativeDiff(),
+ submitSnapshotDiffRequest.hasOrdered() &&
submitSnapshotDiffRequest.getOrdered());
Review Comment:
nit: `getOrdered()` already returns false when the field is unset.
```suggestion
submitSnapshotDiffRequest.getOrdered());
```
--
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]